xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 64648)
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 <pwd.h>
1156766Seric 
1233728Sbostic #ifndef lint
13*64648Seric static char sccsid[] = "@(#)alias.c	8.16 (Berkeley) 09/25/93";
1433728Sbostic #endif /* not lint */
1559673Seric 
1659673Seric 
1760537Seric MAP	*AliasDB[MAXALIASDB + 1];	/* actual database list */
1859673Seric int	NAliasDBs;			/* number of alias databases */
1959673Seric /*
20292Seric **  ALIAS -- Compute aliases.
21292Seric **
229368Seric **	Scans the alias file for an alias for the given address.
239368Seric **	If found, it arranges to deliver to the alias list instead.
249368Seric **	Uses libdbm database if -DDBM.
25292Seric **
26292Seric **	Parameters:
274097Seric **		a -- address to alias.
284999Seric **		sendq -- a pointer to the head of the send queue
294999Seric **			to put the aliases in.
3058092Seric **		e -- the current envelope.
31292Seric **
32292Seric **	Returns:
33292Seric **		none
34292Seric **
35292Seric **	Side Effects:
363185Seric **		Aliases found are expanded.
37292Seric **
38292Seric **	Deficiencies:
39292Seric **		It should complain about names that are aliased to
40292Seric **			nothing.
41292Seric */
42292Seric 
4355012Seric alias(a, sendq, e)
444097Seric 	register ADDRESS *a;
454999Seric 	ADDRESS **sendq;
4655012Seric 	register ENVELOPE *e;
47292Seric {
484081Seric 	register char *p;
4958082Seric 	int naliases;
5058170Seric 	char *owner;
5158170Seric 	char obuf[MAXNAME + 6];
525701Seric 	extern char *aliaslookup();
53292Seric 
547671Seric 	if (tTd(27, 1))
554098Seric 		printf("alias(%s)\n", a->q_paddr);
56292Seric 
574098Seric 	/* don't realias already aliased names */
5858680Seric 	if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags))
594098Seric 		return;
604098Seric 
6159673Seric 	if (NoAlias)
6259673Seric 		return;
6359673Seric 
6455012Seric 	e->e_to = a->q_paddr;
654098Seric 
664314Seric 	/*
674314Seric 	**  Look up this name
684314Seric 	*/
694314Seric 
7059673Seric 	p = aliaslookup(a->q_user, e);
714098Seric 	if (p == NULL)
724098Seric 		return;
73292Seric 
74292Seric 	/*
754098Seric 	**  Match on Alias.
764098Seric 	**	Deliver to the target list.
771515Seric 	*/
781515Seric 
797671Seric 	if (tTd(27, 1))
804098Seric 		printf("%s (%s, %s) aliased to %s\n",
814098Seric 		    a->q_paddr, a->q_host, a->q_user, p);
8258092Seric 	if (bitset(EF_VRFYONLY, e->e_flags))
8358154Seric 	{
8458154Seric 		a->q_flags |= QVERIFIED;
8558884Seric 		e->e_nrcpts++;
8658092Seric 		return;
8758154Seric 	}
8858154Seric 	message("aliased to %s", p);
8957977Seric #ifdef LOG
9058020Seric 	if (LogLevel > 9)
9157977Seric 		syslog(LOG_INFO, "%s: alias %s => %s", e->e_id, a->q_paddr, p);
9257977Seric #endif
9358082Seric 	a->q_flags &= ~QSELFREF;
944098Seric 	AliasLevel++;
9558082Seric 	naliases = sendtolist(p, a, sendq, e);
964098Seric 	AliasLevel--;
9764301Seric 	if (!bitset(QSELFREF, a->q_flags))
9858065Seric 	{
9958065Seric 		if (tTd(27, 5))
10058065Seric 		{
10158065Seric 			printf("alias: QDONTSEND ");
10258065Seric 			printaddr(a, FALSE);
10358065Seric 		}
10458065Seric 		a->q_flags |= QDONTSEND;
10558065Seric 	}
10658170Seric 
10758170Seric 	/*
10858170Seric 	**  Look for owner of alias
10958170Seric 	*/
11058170Seric 
11158170Seric 	(void) strcpy(obuf, "owner-");
11258170Seric 	if (strncmp(a->q_user, "owner-", 6) == 0)
11358170Seric 		(void) strcat(obuf, "owner");
11458170Seric 	else
11558170Seric 		(void) strcat(obuf, a->q_user);
11658170Seric 	if (!bitnset(M_USR_UPPER, a->q_mailer->m_flags))
11758170Seric 		makelower(obuf);
11859673Seric 	owner = aliaslookup(obuf, e);
11958170Seric 	if (owner != NULL)
12058170Seric 	{
12158170Seric 		if (strchr(owner, ',') != NULL)
12258170Seric 			owner = obuf;
12358170Seric 		a->q_owner = newstr(owner);
12458170Seric 	}
1254098Seric }
1264098Seric /*
1275701Seric **  ALIASLOOKUP -- look up a name in the alias file.
1285701Seric **
1295701Seric **	Parameters:
1305701Seric **		name -- the name to look up.
1315701Seric **
1325701Seric **	Returns:
1335701Seric **		the value of name.
1345701Seric **		NULL if unknown.
1355701Seric **
1365701Seric **	Side Effects:
1375701Seric **		none.
1385701Seric **
1395701Seric **	Warnings:
1405701Seric **		The return value will be trashed across calls.
1415701Seric */
1425701Seric 
1435701Seric char *
14459673Seric aliaslookup(name, e)
1455701Seric 	char *name;
14659673Seric 	ENVELOPE *e;
1475701Seric {
14859673Seric 	register int dbno;
14960089Seric 	register MAP *map;
15059673Seric 	register char *p;
1515701Seric 
15259673Seric 	for (dbno = 0; dbno < NAliasDBs; dbno++)
15359673Seric 	{
15460089Seric 		auto int stat;
15560089Seric 
15660537Seric 		map = AliasDB[dbno];
15760207Seric 		if (!bitset(MF_OPEN, map->map_mflags))
15859673Seric 			continue;
15960207Seric 		p = (*map->map_class->map_lookup)(map, name, NULL, &stat);
16059673Seric 		if (p != NULL)
16159673Seric 			return p;
16259673Seric 	}
16359673Seric 	return NULL;
16459673Seric }
16559673Seric /*
16659673Seric **  SETALIAS -- set up an alias map
16759673Seric **
16859673Seric **	Called when reading configuration file.
16959673Seric **
17059673Seric **	Parameters:
17159673Seric **		spec -- the alias specification
17259673Seric **
17359673Seric **	Returns:
17459673Seric **		none.
17559673Seric */
17657381Seric 
17759673Seric setalias(spec)
17859673Seric 	char *spec;
17959673Seric {
18059673Seric 	register char *p;
18160089Seric 	register MAP *map;
18259673Seric 	char *class;
18359673Seric 	STAB *s;
18459673Seric 
18559697Seric 	if (tTd(27, 8))
18659697Seric 		printf("setalias(%s)\n", spec);
18759697Seric 
18859758Seric 	for (p = spec; p != NULL; )
18951756Seric 	{
19060537Seric 		char aname[50];
19160537Seric 
19259758Seric 		while (isspace(*p))
19359758Seric 			p++;
19460502Seric 		if (*p == '\0')
19559673Seric 			break;
19659673Seric 		spec = p;
19759673Seric 
19859758Seric 		if (NAliasDBs >= MAXALIASDB)
19959758Seric 		{
20059758Seric 			syserr("Too many alias databases defined, %d max", MAXALIASDB);
20159758Seric 			return;
20259758Seric 		}
20360537Seric 		(void) sprintf(aname, "Alias%d", NAliasDBs);
20460537Seric 		s = stab(aname, ST_MAP, ST_ENTER);
20560537Seric 		map = &s->s_map;
20660537Seric 		AliasDB[NAliasDBs] = map;
20760089Seric 		bzero(map, sizeof *map);
20859758Seric 
20959758Seric 		p = strpbrk(p, " ,/:");
21059758Seric 		if (p != NULL && *p == ':')
21159758Seric 		{
21260089Seric 			/* map name */
21359758Seric 			*p++ = '\0';
21459758Seric 			class = spec;
21559758Seric 			spec = p;
21659758Seric 		}
21759758Seric 		else
21859758Seric 		{
21959758Seric 			class = "implicit";
22060228Seric 			map->map_mflags = MF_OPTIONAL|MF_INCLNULL;
22159758Seric 		}
22259758Seric 
22359758Seric 		/* find end of spec */
22459758Seric 		if (p != NULL)
22559758Seric 			p = strchr(p, ',');
22659758Seric 		if (p != NULL)
22759758Seric 			*p++ = '\0';
22859758Seric 
22959758Seric 		/* look up class */
23060089Seric 		s = stab(class, ST_MAPCLASS, ST_FIND);
23159758Seric 		if (s == NULL)
23259758Seric 		{
23359758Seric 			if (tTd(27, 1))
23459758Seric 				printf("Unknown alias class %s\n", class);
23559758Seric 		}
23660207Seric 		else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
23760207Seric 		{
23860207Seric 			syserr("setalias: map class %s can't handle aliases",
23960207Seric 				class);
24060207Seric 		}
24159758Seric 		else
24259758Seric 		{
24360207Seric 			map->map_class = &s->s_mapclass;
24460089Seric 			if (map->map_class->map_parse(map, spec))
24560089Seric 			{
24660207Seric 				map->map_mflags |= MF_VALID|MF_ALIAS;
24760089Seric 				NAliasDBs++;
24860089Seric 			}
24959758Seric 		}
25059756Seric 	}
2515701Seric }
2525701Seric /*
25359673Seric **  ALIASWAIT -- wait for distinguished @:@ token to appear.
25459673Seric **
25559673Seric **	This can decide to reopen or rebuild the alias file
25659673Seric */
25759673Seric 
25860207Seric aliaswait(map, ext)
25960089Seric 	MAP *map;
26060207Seric 	char *ext;
26159673Seric {
26259673Seric 	int atcnt;
26359673Seric 	time_t mtime;
26459673Seric 	struct stat stb;
26559673Seric 	char buf[MAXNAME];
26659673Seric 
26759697Seric 	if (tTd(27, 3))
26860207Seric 		printf("aliaswait(%s:%s)\n",
26960207Seric 			map->map_class->map_cname, map->map_file);
270*64648Seric 	if (bitset(MF_ALIASWAIT, map->map_mflags))
271*64648Seric 		return;
272*64648Seric 	map->map_mflags |= MF_ALIASWAIT;
27359697Seric 
27417471Seric 	atcnt = SafeAlias * 2;
27517471Seric 	if (atcnt > 0)
27617471Seric 	{
27760089Seric 		auto int st;
27860089Seric 
27959673Seric 		while (atcnt-- >= 0 &&
28060089Seric 		       map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
28125689Seric 		{
28225689Seric 			/*
28359673Seric 			**  Close and re-open the alias database in case
28459673Seric 			**  the one is mv'ed instead of cp'ed in.
28525689Seric 			*/
28625689Seric 
28759697Seric 			if (tTd(27, 2))
28859697Seric 				printf("aliaswait: sleeping\n");
28959697Seric 
29060089Seric 			map->map_class->map_close(map);
29117471Seric 			sleep(30);
29260089Seric 			map->map_class->map_open(map, O_RDONLY);
29325689Seric 		}
29417471Seric 	}
2958437Seric 
29659673Seric 	/* see if we need to go into auto-rebuild mode */
29760207Seric 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
29860207Seric 	{
29960207Seric 		if (tTd(27, 3))
30060207Seric 			printf("aliaswait: not rebuildable\n");
301*64648Seric 		map->map_mflags &= ~MF_ALIASWAIT;
30259673Seric 		return;
30360207Seric 	}
30460207Seric 	if (stat(map->map_file, &stb) < 0)
30560207Seric 	{
30660207Seric 		if (tTd(27, 3))
30760207Seric 			printf("aliaswait: no source file\n");
308*64648Seric 		map->map_mflags &= ~MF_ALIASWAIT;
30960207Seric 		return;
31060207Seric 	}
31159673Seric 	mtime = stb.st_mtime;
31260089Seric 	(void) strcpy(buf, map->map_file);
31360207Seric 	if (ext != NULL)
31460207Seric 		(void) strcat(buf, ext);
31559673Seric 	if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || atcnt < 0)
3164322Seric 	{
31759673Seric 		/* database is out of date */
31840559Sbostic 		if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
3194322Seric 		{
32060089Seric 			message("auto-rebuilding alias database %s", buf);
32160207Seric 			rebuildaliases(map, TRUE);
3224322Seric 		}
3234322Seric 		else
3244322Seric 		{
32519039Seric #ifdef LOG
32658020Seric 			if (LogLevel > 3)
32759673Seric 				syslog(LOG_INFO, "alias database %s out of date",
32860089Seric 					buf);
32956795Seric #endif /* LOG */
33060089Seric 			message("Warning: alias database %s out of date", buf);
3314322Seric 		}
3324322Seric 	}
333*64648Seric 	map->map_mflags &= ~MF_ALIASWAIT;
33459673Seric }
33559673Seric /*
33659673Seric **  REBUILDALIASES -- rebuild the alias database.
33759673Seric **
33859673Seric **	Parameters:
33960089Seric **		map -- the database to rebuild.
34059673Seric **		automatic -- set if this was automatically generated.
34159673Seric **
34259673Seric **	Returns:
34359673Seric **		none.
34459673Seric **
34559673Seric **	Side Effects:
34659673Seric **		Reads the text version of the database, builds the
34759673Seric **		DBM or DB version.
34859673Seric */
3494322Seric 
35060207Seric rebuildaliases(map, automatic)
35160089Seric 	register MAP *map;
35259673Seric 	bool automatic;
35359673Seric {
35459673Seric 	FILE *af;
35564388Seric 	bool nolock = FALSE;
35664564Seric 	sigfunc_t oldsigint;
3574322Seric 
35860207Seric 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
35959673Seric 		return;
3604322Seric 
36159673Seric 	/* try to lock the source file */
36260089Seric 	if ((af = fopen(map->map_file, "r+")) == NULL)
36359673Seric 	{
36464388Seric 		if (errno != EACCES || automatic ||
36564388Seric 		    (af = fopen(map->map_file, "r")) == NULL)
36664388Seric 		{
36764388Seric 			int saveerr = errno;
36864382Seric 
36964388Seric 			if (tTd(27, 1))
37064388Seric 				printf("Can't open %s: %s\n",
37164388Seric 					map->map_file, errstring(saveerr));
37264388Seric 			if (!automatic)
37364388Seric 				message("newaliases: cannot open %s: %s",
37464388Seric 					map->map_file, errstring(saveerr));
37564388Seric 			errno = 0;
37664388Seric 			return;
37764388Seric 		}
37864388Seric 		nolock = TRUE;
37964388Seric 		message("warning: cannot lock %s: %s",
38064388Seric 			map->map_file, errstring(errno));
3818437Seric 	}
38259673Seric 
38359673Seric 	/* see if someone else is rebuilding the alias file */
38464388Seric 	if (!nolock &&
38564388Seric 	    !lockfile(fileno(af), map->map_file, NULL, LOCK_EX|LOCK_NB))
38659673Seric 	{
38759673Seric 		/* yes, they are -- wait until done */
38859673Seric 		message("Alias file %s is already being rebuilt",
38960089Seric 			map->map_file);
39059673Seric 		if (OpMode != MD_INITALIAS)
39159673Seric 		{
39259673Seric 			/* wait for other rebuild to complete */
39364335Seric 			(void) lockfile(fileno(af), map->map_file, NULL,
39459673Seric 					LOCK_EX);
39559673Seric 		}
39659673Seric 		(void) fclose(af);
39759673Seric 		errno = 0;
39859673Seric 		return;
39959673Seric 	}
40059673Seric 
40164035Seric 	oldsigint = setsignal(SIGINT, SIG_IGN);
40259673Seric 
40360207Seric 	if (map->map_class->map_open(map, O_RDWR))
40460089Seric 	{
40564382Seric #ifdef LOG
40664382Seric 		if (LogLevel > 7)
40764382Seric 		{
40864382Seric 			syslog(LOG_NOTICE, "alias database %s %srebuilt by %s",
40964382Seric 				map->map_file, automatic ? "auto" : "",
41064382Seric 				username());
41164382Seric 		}
41264382Seric #endif /* LOG */
41360207Seric 		map->map_mflags |= MF_OPEN|MF_WRITABLE;
41460207Seric 		readaliases(map, af, automatic);
41560089Seric 	}
41660207Seric 	else
41760207Seric 	{
41860207Seric 		if (tTd(27, 1))
41960207Seric 			printf("Can't create database for %s: %s\n",
42060207Seric 				map->map_file, errstring(errno));
42160207Seric 		if (!automatic)
42260207Seric 			syserr("Cannot create database for alias file %s",
42360207Seric 				map->map_file);
42460207Seric 	}
42559673Seric 
42659673Seric 	/* close the file, thus releasing locks */
42759673Seric 	fclose(af);
42859673Seric 
42959673Seric 	/* add distinguished entries and close the database */
43060207Seric 	if (bitset(MF_OPEN, map->map_mflags))
43160089Seric 		map->map_class->map_close(map);
43259673Seric 
43359673Seric 	/* restore the old signal */
43464035Seric 	(void) setsignal(SIGINT, oldsigint);
4354157Seric }
4364157Seric /*
4374157Seric **  READALIASES -- read and process the alias file.
4384157Seric **
4394157Seric **	This routine implements the part of initaliases that occurs
4404157Seric **	when we are not going to use the DBM stuff.
4414157Seric **
4424157Seric **	Parameters:
44360089Seric **		map -- the alias database descriptor.
44459673Seric **		af -- file to read the aliases from.
44559733Seric **		automatic -- set if this was an automatic rebuild.
4464157Seric **
4474157Seric **	Returns:
4484157Seric **		none.
4494157Seric **
4504157Seric **	Side Effects:
4514157Seric **		Reads aliasfile into the symbol table.
4524157Seric **		Optionally, builds the .dir & .pag files.
4534157Seric */
4544157Seric 
45560207Seric readaliases(map, af, automatic)
45660089Seric 	register MAP *map;
45759673Seric 	FILE *af;
45859733Seric 	int automatic;
4594157Seric {
4604098Seric 	register char *p;
4614098Seric 	char *rhs;
4624098Seric 	bool skipping;
46359673Seric 	long naliases, bytes, longest;
4644098Seric 	ADDRESS al, bl;
4659368Seric 	char line[BUFSIZ];
4664098Seric 
4674314Seric 	/*
4684314Seric 	**  Read and interpret lines
4694314Seric 	*/
4704314Seric 
47160089Seric 	FileName = map->map_file;
4729368Seric 	LineNumber = 0;
4734322Seric 	naliases = bytes = longest = 0;
4744098Seric 	skipping = FALSE;
4754098Seric 	while (fgets(line, sizeof (line), af) != NULL)
4764098Seric 	{
4774322Seric 		int lhssize, rhssize;
4784322Seric 
4799368Seric 		LineNumber++;
48056795Seric 		p = strchr(line, '\n');
48125278Seric 		if (p != NULL)
48225278Seric 			*p = '\0';
4834098Seric 		switch (line[0])
4844098Seric 		{
4854098Seric 		  case '#':
4864098Seric 		  case '\0':
4874098Seric 			skipping = FALSE;
4884098Seric 			continue;
4894065Seric 
4904098Seric 		  case ' ':
4914098Seric 		  case '\t':
4924098Seric 			if (!skipping)
49358151Seric 				syserr("554 Non-continuation line starts with space");
4944098Seric 			skipping = TRUE;
4954097Seric 			continue;
4964098Seric 		}
4974098Seric 		skipping = FALSE;
4981874Seric 
4994314Seric 		/*
5004314Seric 		**  Process the LHS
50157736Seric 		**	Find the colon separator, and parse the address.
50216898Seric 		**	It should resolve to a local name -- this will
50316898Seric 		**	be checked later (we want to optionally do
50416898Seric 		**	parsing of the RHS first to maximize error
50516898Seric 		**	detection).
5064314Seric 		*/
5074314Seric 
5084098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
5094097Seric 			continue;
51016898Seric 		if (*p++ != ':')
5114098Seric 		{
51258151Seric 			syserr("554 missing colon");
5134097Seric 			continue;
5144098Seric 		}
51564284Seric 		if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv) == NULL)
5164098Seric 		{
51764278Seric 			syserr("554 %s... illegal alias name", al.q_paddr);
51816898Seric 			continue;
5194098Seric 		}
5204314Seric 
5214314Seric 		/*
5224314Seric 		**  Process the RHS.
5234314Seric 		**	'al' is the internal form of the LHS address.
5244314Seric 		**	'p' points to the text of the RHS.
5254314Seric 		*/
5264314Seric 
52758914Seric 		while (isascii(*p) && isspace(*p))
52858914Seric 			p++;
5294098Seric 		rhs = p;
5304098Seric 		for (;;)
5314098Seric 		{
5324098Seric 			register char c;
53358662Seric 			register char *nlp;
5341515Seric 
53558662Seric 			nlp = &p[strlen(p)];
53658662Seric 			if (nlp[-1] == '\n')
53758662Seric 				*--nlp = '\0';
53858662Seric 
53959673Seric 			if (CheckAliases)
5404098Seric 			{
5414157Seric 				/* do parsing & compression of addresses */
54225278Seric 				while (*p != '\0')
5434098Seric 				{
54458333Seric 					auto char *delimptr;
54525278Seric 
54658050Seric 					while ((isascii(*p) && isspace(*p)) ||
54758050Seric 								*p == ',')
5484157Seric 						p++;
54925278Seric 					if (*p == '\0')
55025278Seric 						break;
55164284Seric 					if (parseaddr(p, &bl, RF_COPYNONE, ',',
55264284Seric 						      &delimptr, CurEnv) == NULL)
55358151Seric 						usrerr("553 %s... bad address", p);
55458333Seric 					p = delimptr;
5554098Seric 				}
5564098Seric 			}
5574157Seric 			else
55815769Seric 			{
55958662Seric 				p = nlp;
56015769Seric 			}
5611515Seric 
5624098Seric 			/* see if there should be a continuation line */
5634106Seric 			c = fgetc(af);
5644106Seric 			if (!feof(af))
5654314Seric 				(void) ungetc(c, af);
5664106Seric 			if (c != ' ' && c != '\t')
5674098Seric 				break;
5684098Seric 
5694098Seric 			/* read continuation line */
5704098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
5714098Seric 				break;
5729368Seric 			LineNumber++;
57357135Seric 
57457135Seric 			/* check for line overflow */
57557135Seric 			if (strchr(p, '\n') == NULL)
57657135Seric 			{
57758151Seric 				usrerr("554 alias too long");
57857135Seric 				break;
57957135Seric 			}
5804098Seric 		}
58116898Seric 		if (al.q_mailer != LocalMailer)
58216898Seric 		{
58364278Seric 			syserr("554 %s... cannot alias non-local names",
58464278Seric 				al.q_paddr);
58516898Seric 			continue;
58616898Seric 		}
5874314Seric 
5884314Seric 		/*
5894314Seric 		**  Insert alias into symbol table or DBM file
5904314Seric 		*/
5914314Seric 
59257381Seric 		if (!bitnset(M_USR_UPPER, al.q_mailer->m_flags))
59357381Seric 			makelower(al.q_user);
5944322Seric 
59559673Seric 		lhssize = strlen(al.q_user);
59659673Seric 		rhssize = strlen(rhs);
59760089Seric 		map->map_class->map_store(map, al.q_user, rhs);
5984157Seric 
59959673Seric 		if (al.q_paddr != NULL)
60059673Seric 			free(al.q_paddr);
60159673Seric 		if (al.q_host != NULL)
60259673Seric 			free(al.q_host);
60359673Seric 		if (al.q_user != NULL)
60459673Seric 			free(al.q_user);
6054322Seric 
6064322Seric 		/* statistics */
6074322Seric 		naliases++;
6084322Seric 		bytes += lhssize + rhssize;
6094322Seric 		if (rhssize > longest)
6104322Seric 			longest = rhssize;
6111515Seric 	}
61219784Seric 
61360207Seric 	CurEnv->e_to = NULL;
61459673Seric 	FileName = NULL;
61559733Seric 	if (Verbose || !automatic)
61659733Seric 		message("%s: %d aliases, longest %d bytes, %d bytes total",
61760089Seric 			map->map_file, naliases, longest, bytes);
61859673Seric # ifdef LOG
61959673Seric 	if (LogLevel > 7)
62059673Seric 		syslog(LOG_INFO, "%s: %d aliases, longest %d bytes, %d bytes total",
62160089Seric 			map->map_file, naliases, longest, bytes);
62259673Seric # endif /* LOG */
62359673Seric }
62459673Seric /*
625292Seric **  FORWARD -- Try to forward mail
626292Seric **
627292Seric **	This is similar but not identical to aliasing.
628292Seric **
629292Seric **	Parameters:
6304314Seric **		user -- the name of the user who's mail we would like
6314314Seric **			to forward to.  It must have been verified --
6324314Seric **			i.e., the q_home field must have been filled
6334314Seric **			in.
6344999Seric **		sendq -- a pointer to the head of the send queue to
6354999Seric **			put this user's aliases in.
636292Seric **
637292Seric **	Returns:
6384098Seric **		none.
639292Seric **
640292Seric **	Side Effects:
6413185Seric **		New names are added to send queues.
642292Seric */
643292Seric 
64455012Seric forward(user, sendq, e)
6452966Seric 	ADDRESS *user;
6464999Seric 	ADDRESS **sendq;
64755012Seric 	register ENVELOPE *e;
648292Seric {
64957136Seric 	char *pp;
65057136Seric 	char *ep;
6514069Seric 
6527671Seric 	if (tTd(27, 1))
6534098Seric 		printf("forward(%s)\n", user->q_paddr);
6544098Seric 
6554594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
6564098Seric 		return;
6574314Seric 	if (user->q_home == NULL)
65858059Seric 	{
65958151Seric 		syserr("554 forward: no home");
66058059Seric 		user->q_home = "/nosuchdirectory";
66158059Seric 	}
6624069Seric 
6634069Seric 	/* good address -- look for .forward file in home */
66455012Seric 	define('z', user->q_home, e);
66557136Seric 	define('u', user->q_user, e);
66657136Seric 	define('h', user->q_host, e);
66757136Seric 	if (ForwardPath == NULL)
66858050Seric 		ForwardPath = newstr("\201z/.forward");
66957136Seric 
67057136Seric 	for (pp = ForwardPath; pp != NULL; pp = ep)
67157136Seric 	{
67258247Seric 		int err;
67357232Seric 		char buf[MAXPATHLEN+1];
67457136Seric 
67557136Seric 		ep = strchr(pp, ':');
67657136Seric 		if (ep != NULL)
67757136Seric 			*ep = '\0';
67857136Seric 		expand(pp, buf, &buf[sizeof buf - 1], e);
67957136Seric 		if (ep != NULL)
68057136Seric 			*ep++ = ':';
68157136Seric 		if (tTd(27, 3))
68257136Seric 			printf("forward: trying %s\n", buf);
68363753Seric 
68458247Seric 		err = include(buf, TRUE, user, sendq, e);
68558247Seric 		if (err == 0)
68657136Seric 			break;
68764325Seric 		else if (transienterror(err))
68858247Seric 		{
68958247Seric 			/* we have to suspend this message */
69059563Seric 			if (tTd(27, 2))
69159563Seric 				printf("forward: transient error on %s\n", buf);
69259563Seric #ifdef LOG
69359563Seric 			if (LogLevel > 2)
69459624Seric 				syslog(LOG_ERR, "%s: forward %s: transient error: %s",
69559624Seric 					e->e_id, buf, errstring(err));
69659563Seric #endif
69759611Seric 			message("%s: %s: message queued", buf, errstring(err));
69863853Seric 			user->q_flags |= QQUEUEUP;
69958247Seric 			return;
70058247Seric 		}
70157136Seric 	}
702292Seric }
703