xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 60089)
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 
958332Seric # include "sendmail.h"
1050577Seric # include <signal.h>
1150577Seric # include <pwd.h>
1256766Seric 
1333728Sbostic #ifndef lint
14*60089Seric static char sccsid[] = "@(#)alias.c	6.46 (Berkeley) 05/17/93";
1533728Sbostic #endif /* not lint */
1659673Seric 
1759673Seric 
18*60089Seric 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;
150*60089Seric 	register MAP *map;
15159673Seric 	register char *p;
152*60089Seric 	char *nullargv[1];
1535701Seric 
154*60089Seric 	nullargv[0] = NULL;
15559673Seric 	for (dbno = 0; dbno < NAliasDBs; dbno++)
15659673Seric 	{
157*60089Seric 		auto int stat;
158*60089Seric 
159*60089Seric 		map = &AliasDB[dbno];
160*60089Seric 		if (!bitset(MF_VALID, map->map_flags))
16159673Seric 			continue;
162*60089Seric 		p = (*map->map_class->map_lookup)(map, name, nullargv, &stat);
16359673Seric 		if (p != NULL)
16459673Seric 			return p;
16559673Seric 	}
16659673Seric 	return NULL;
16759673Seric }
16859673Seric /*
16959673Seric **  SETALIAS -- set up an alias map
17059673Seric **
17159673Seric **	Called when reading configuration file.
17259673Seric **
17359673Seric **	Parameters:
17459673Seric **		spec -- the alias specification
17559673Seric **
17659673Seric **	Returns:
17759673Seric **		none.
17859673Seric */
17957381Seric 
18059673Seric setalias(spec)
18159673Seric 	char *spec;
18259673Seric {
18359673Seric 	register char *p;
184*60089Seric 	register MAP *map;
18559673Seric 	char *class;
18659673Seric 	STAB *s;
18759673Seric 
18859697Seric 	if (tTd(27, 8))
18959697Seric 		printf("setalias(%s)\n", spec);
19059697Seric 
19159758Seric 	for (p = spec; p != NULL; )
19251756Seric 	{
19359758Seric 		while (isspace(*p))
19459758Seric 			p++;
19559758Seric 		if (*p == NULL)
19659673Seric 			break;
19759673Seric 		spec = p;
19859673Seric 
19959758Seric 		if (NAliasDBs >= MAXALIASDB)
20059758Seric 		{
20159758Seric 			syserr("Too many alias databases defined, %d max", MAXALIASDB);
20259758Seric 			return;
20359758Seric 		}
204*60089Seric 		map = &AliasDB[NAliasDBs];
205*60089Seric 		bzero(map, sizeof *map);
20659758Seric 
20759758Seric 		p = strpbrk(p, " ,/:");
20859758Seric 		if (p != NULL && *p == ':')
20959758Seric 		{
210*60089Seric 			/* map name */
21159758Seric 			*p++ = '\0';
21259758Seric 			class = spec;
21359758Seric 			spec = p;
21459758Seric 		}
21559758Seric 		else
21659758Seric 		{
21759758Seric 			class = "implicit";
218*60089Seric 			map->map_flags = MF_OPTIONAL;
21959758Seric 		}
22059758Seric 
22159758Seric 		/* find end of spec */
22259758Seric 		if (p != NULL)
22359758Seric 			p = strchr(p, ',');
22459758Seric 		if (p != NULL)
22559758Seric 			*p++ = '\0';
22659758Seric 
22759758Seric 		/* look up class */
228*60089Seric 		s = stab(class, ST_MAPCLASS, ST_FIND);
22959758Seric 		if (s == NULL)
23059758Seric 		{
23159758Seric 			if (tTd(27, 1))
23259758Seric 				printf("Unknown alias class %s\n", class);
23359758Seric 		}
23459758Seric 		else
23559758Seric 		{
236*60089Seric 			map->map_class = s->s_mapclass;
237*60089Seric 			if (map->map_class->map_parse(map, spec))
238*60089Seric 			{
239*60089Seric 				map->map_flags |= MF_VALID;
240*60089Seric 				NAliasDBs++;
241*60089Seric 			}
24259758Seric 		}
24359756Seric 	}
2445701Seric }
2455701Seric /*
2464098Seric **  INITALIASES -- initialize for aliasing
2474098Seric **
24856845Seric **	Very different depending on whether we are running NDBM or not.
2494098Seric **
2504098Seric **	Parameters:
25159673Seric **		rebuild -- if TRUE, this rebuilds the cached versions.
25259673Seric **		e -- current envelope.
2534098Seric **
2544098Seric **	Returns:
2554098Seric **		none.
2564098Seric **
2574098Seric **	Side Effects:
2584098Seric **		initializes aliases:
25956845Seric **		if NDBM:  opens the database.
26056845Seric **		if ~NDBM: reads the aliases into the symbol table.
2614098Seric */
2624098Seric 
26340559Sbostic # define DBMMODE	0644
2644157Seric 
26559673Seric initaliases(rebuild, e)
26659673Seric 	bool rebuild;
26755012Seric 	register ENVELOPE *e;
2684098Seric {
26959673Seric 	int dbno;
270*60089Seric 	register MAP *map;
2714322Seric 
27259673Seric 	for (dbno = 0; dbno < NAliasDBs; dbno++)
2738437Seric 	{
274*60089Seric 		map = &AliasDB[dbno];
27559697Seric 
27659697Seric 		if (tTd(27, 2))
27759697Seric 			printf("initaliases(%s:%s)\n",
278*60089Seric 				map->map_class->map_cname, map->map_file);
27959697Seric 
28059673Seric 		if (rebuild)
28150576Seric 		{
282*60089Seric 			rebuildaliases(map, FALSE, e);
28350576Seric 		}
28459673Seric 		else
28557249Seric 		{
286*60089Seric 			if (map->map_class->map_open(map, O_RDONLY))
28759756Seric 			{
28859756Seric 				if (tTd(27, 4))
28959756Seric 					printf("%s:%s: valid\n",
290*60089Seric 						map->map_class->map_cname,
291*60089Seric 						map->map_file);
292*60089Seric 				map->map_flags |= MF_VALID;
293*60089Seric 				aliaswait(map, e);
29459756Seric 			}
29559756Seric 			else if (tTd(27, 4))
29659756Seric 				printf("%s:%s: invalid: %s\n",
297*60089Seric 					map->map_class->map_cname, map->map_file,
29859756Seric 					errstring(errno));
29957249Seric 		}
30050575Seric 	}
30159673Seric }
30259673Seric /*
30359673Seric **  ALIASWAIT -- wait for distinguished @:@ token to appear.
30459673Seric **
30559673Seric **	This can decide to reopen or rebuild the alias file
30659673Seric */
30759673Seric 
308*60089Seric aliaswait(map, e)
309*60089Seric 	MAP *map;
31059673Seric 	ENVELOPE *e;
31159673Seric {
31259673Seric 	int atcnt;
31359673Seric 	time_t mtime;
31459673Seric 	struct stat stb;
31559673Seric 	char buf[MAXNAME];
31659673Seric 
31759697Seric 	if (tTd(27, 3))
31859697Seric 		printf("aliaswait\n");
31959697Seric 
32017471Seric 	atcnt = SafeAlias * 2;
32117471Seric 	if (atcnt > 0)
32217471Seric 	{
323*60089Seric 		auto int st;
324*60089Seric 
32559673Seric 		while (atcnt-- >= 0 &&
326*60089Seric 		       map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
32725689Seric 		{
32825689Seric 			/*
32959673Seric 			**  Close and re-open the alias database in case
33059673Seric 			**  the one is mv'ed instead of cp'ed in.
33125689Seric 			*/
33225689Seric 
33359697Seric 			if (tTd(27, 2))
33459697Seric 				printf("aliaswait: sleeping\n");
33559697Seric 
336*60089Seric 			map->map_class->map_close(map);
33717471Seric 			sleep(30);
338*60089Seric 			map->map_class->map_open(map, O_RDONLY);
33925689Seric 		}
34017471Seric 	}
3418437Seric 
34259673Seric 	/* see if we need to go into auto-rebuild mode */
343*60089Seric 	if (map->map_class->map_rebuild == NULL ||
344*60089Seric 	    stat(map->map_file, &stb) < 0)
34559673Seric 		return;
34659673Seric 	mtime = stb.st_mtime;
347*60089Seric 	(void) strcpy(buf, map->map_file);
348*60089Seric 	if (map->map_class->map_ext != NULL)
349*60089Seric 		(void) strcat(buf, map->map_class->map_ext);
35059673Seric 	if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || atcnt < 0)
3514322Seric 	{
35259673Seric 		/* database is out of date */
35340559Sbostic 		if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
3544322Seric 		{
355*60089Seric 			message("auto-rebuilding alias database %s", buf);
356*60089Seric 			rebuildaliases(map, TRUE, e);
3574322Seric 		}
3584322Seric 		else
3594322Seric 		{
36019039Seric #ifdef LOG
36158020Seric 			if (LogLevel > 3)
36259673Seric 				syslog(LOG_INFO, "alias database %s out of date",
363*60089Seric 					buf);
36456795Seric #endif /* LOG */
365*60089Seric 			message("Warning: alias database %s out of date", buf);
3664322Seric 		}
3674322Seric 	}
36859673Seric }
36959673Seric /*
37059673Seric **  REBUILDALIASES -- rebuild the alias database.
37159673Seric **
37259673Seric **	Parameters:
373*60089Seric **		map -- the database to rebuild.
37459673Seric **		automatic -- set if this was automatically generated.
37559673Seric **		e -- current envelope.
37659673Seric **
37759673Seric **	Returns:
37859673Seric **		none.
37959673Seric **
38059673Seric **	Side Effects:
38159673Seric **		Reads the text version of the database, builds the
38259673Seric **		DBM or DB version.
38359673Seric */
3844322Seric 
385*60089Seric rebuildaliases(map, automatic, e)
386*60089Seric 	register MAP *map;
38759673Seric 	bool automatic;
38859673Seric 	register ENVELOPE *e;
38959673Seric {
39059673Seric 	FILE *af;
39159673Seric 	void (*oldsigint)();
3924322Seric 
393*60089Seric 	if (map->map_class->map_rebuild == NULL)
39459673Seric 		return;
3954322Seric 
39659673Seric #ifdef LOG
39759673Seric 	if (LogLevel > 7)
3988437Seric 	{
39959673Seric 		extern char *username();
40025522Seric 
40159673Seric 		syslog(LOG_NOTICE, "alias database %s %srebuilt by %s",
402*60089Seric 			map->map_file, automatic ? "auto" : "", username());
40359673Seric 	}
40456795Seric #endif /* LOG */
40559673Seric 
40659673Seric 	/* try to lock the source file */
407*60089Seric 	if ((af = fopen(map->map_file, "r+")) == NULL)
40859673Seric 	{
40959756Seric 		if (tTd(27, 1))
41059756Seric 			printf("Can't open %s: %s\n",
411*60089Seric 				map->map_file, errstring(errno));
412*60089Seric 		map->map_flags &= ~MF_VALID;
41359673Seric 		errno = 0;
41459673Seric 		return;
4158437Seric 	}
41659673Seric 
41759673Seric 	/* see if someone else is rebuilding the alias file */
418*60089Seric 	if (!lockfile(fileno(af), map->map_file, LOCK_EX|LOCK_NB))
41959673Seric 	{
42059673Seric 		/* yes, they are -- wait until done */
42159673Seric 		message("Alias file %s is already being rebuilt",
422*60089Seric 			map->map_file);
42359673Seric 		if (OpMode != MD_INITALIAS)
42459673Seric 		{
42559673Seric 			/* wait for other rebuild to complete */
426*60089Seric 			(void) lockfile(fileno(af), map->map_file,
42759673Seric 					LOCK_EX);
42859673Seric 		}
42959673Seric 		(void) fclose(af);
43059673Seric 		errno = 0;
43159673Seric 		return;
43259673Seric 	}
43359673Seric 
43459673Seric 	oldsigint = signal(SIGINT, SIG_IGN);
43559673Seric 
436*60089Seric 	map->map_class->map_open(map, O_RDWR);
437*60089Seric 	if (bitset(MF_VALID, map->map_flags))
438*60089Seric 	{
439*60089Seric 		map->map_class->map_rebuild(map, af, automatic);
440*60089Seric 		readaliases(map, af, automatic, e);
441*60089Seric 	}
44259673Seric 
44359673Seric 	/* close the file, thus releasing locks */
44459673Seric 	fclose(af);
44559673Seric 
44659673Seric 	/* add distinguished entries and close the database */
447*60089Seric 	if (bitset(MF_VALID, map->map_flags))
448*60089Seric 		map->map_class->map_close(map);
44959673Seric 
45059673Seric 	/* restore the old signal */
45159673Seric 	(void) signal(SIGINT, oldsigint);
4524157Seric }
4534157Seric /*
4544157Seric **  READALIASES -- read and process the alias file.
4554157Seric **
4564157Seric **	This routine implements the part of initaliases that occurs
4574157Seric **	when we are not going to use the DBM stuff.
4584157Seric **
4594157Seric **	Parameters:
460*60089Seric **		map -- the alias database descriptor.
46159673Seric **		af -- file to read the aliases from.
46259733Seric **		automatic -- set if this was an automatic rebuild.
463*60089Seric **		e -- the current envelope.
4644157Seric **
4654157Seric **	Returns:
4664157Seric **		none.
4674157Seric **
4684157Seric **	Side Effects:
4694157Seric **		Reads aliasfile into the symbol table.
4704157Seric **		Optionally, builds the .dir & .pag files.
4714157Seric */
4724157Seric 
473*60089Seric readaliases(map, af, automatic, e)
474*60089Seric 	register MAP *map;
47559673Seric 	FILE *af;
47659733Seric 	int automatic;
47755012Seric 	register ENVELOPE *e;
4784157Seric {
4794098Seric 	register char *p;
4804098Seric 	char *rhs;
4814098Seric 	bool skipping;
48259673Seric 	long naliases, bytes, longest;
4834098Seric 	ADDRESS al, bl;
4844106Seric 	register STAB *s;
4859368Seric 	char line[BUFSIZ];
4864098Seric 
4874314Seric 	/*
4884314Seric 	**  Read and interpret lines
4894314Seric 	*/
4904314Seric 
491*60089Seric 	FileName = map->map_file;
4929368Seric 	LineNumber = 0;
4934322Seric 	naliases = bytes = longest = 0;
4944098Seric 	skipping = FALSE;
4954098Seric 	while (fgets(line, sizeof (line), af) != NULL)
4964098Seric 	{
4974322Seric 		int lhssize, rhssize;
4984322Seric 
4999368Seric 		LineNumber++;
50056795Seric 		p = strchr(line, '\n');
50125278Seric 		if (p != NULL)
50225278Seric 			*p = '\0';
5034098Seric 		switch (line[0])
5044098Seric 		{
5054098Seric 		  case '#':
5064098Seric 		  case '\0':
5074098Seric 			skipping = FALSE;
5084098Seric 			continue;
5094065Seric 
5104098Seric 		  case ' ':
5114098Seric 		  case '\t':
5124098Seric 			if (!skipping)
51358151Seric 				syserr("554 Non-continuation line starts with space");
5144098Seric 			skipping = TRUE;
5154097Seric 			continue;
5164098Seric 		}
5174098Seric 		skipping = FALSE;
5181874Seric 
5194314Seric 		/*
5204314Seric 		**  Process the LHS
52157736Seric 		**	Find the colon separator, and parse the address.
52216898Seric 		**	It should resolve to a local name -- this will
52316898Seric 		**	be checked later (we want to optionally do
52416898Seric 		**	parsing of the RHS first to maximize error
52516898Seric 		**	detection).
5264314Seric 		*/
5274314Seric 
5284098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
5294097Seric 			continue;
53016898Seric 		if (*p++ != ':')
5314098Seric 		{
53258151Seric 			syserr("554 missing colon");
5334097Seric 			continue;
5344098Seric 		}
53558333Seric 		if (parseaddr(line, &al, 1, ':', NULL, e) == NULL)
5364098Seric 		{
53758151Seric 			syserr("554 illegal alias name");
53816898Seric 			continue;
5394098Seric 		}
5404314Seric 
5414314Seric 		/*
5424314Seric 		**  Process the RHS.
5434314Seric 		**	'al' is the internal form of the LHS address.
5444314Seric 		**	'p' points to the text of the RHS.
5454314Seric 		*/
5464314Seric 
54758914Seric 		while (isascii(*p) && isspace(*p))
54858914Seric 			p++;
5494098Seric 		rhs = p;
5504098Seric 		for (;;)
5514098Seric 		{
5524098Seric 			register char c;
55358662Seric 			register char *nlp;
5541515Seric 
55558662Seric 			nlp = &p[strlen(p)];
55658662Seric 			if (nlp[-1] == '\n')
55758662Seric 				*--nlp = '\0';
55858662Seric 
55959673Seric 			if (CheckAliases)
5604098Seric 			{
5614157Seric 				/* do parsing & compression of addresses */
56225278Seric 				while (*p != '\0')
5634098Seric 				{
56458333Seric 					auto char *delimptr;
56525278Seric 
56658050Seric 					while ((isascii(*p) && isspace(*p)) ||
56758050Seric 								*p == ',')
5684157Seric 						p++;
56925278Seric 					if (*p == '\0')
57025278Seric 						break;
57158333Seric 					if (parseaddr(p, &bl, -1, ',', &delimptr, e) == NULL)
57258151Seric 						usrerr("553 %s... bad address", p);
57358333Seric 					p = delimptr;
5744098Seric 				}
5754098Seric 			}
5764157Seric 			else
57715769Seric 			{
57858662Seric 				p = nlp;
57915769Seric 			}
5801515Seric 
5814098Seric 			/* see if there should be a continuation line */
5824106Seric 			c = fgetc(af);
5834106Seric 			if (!feof(af))
5844314Seric 				(void) ungetc(c, af);
5854106Seric 			if (c != ' ' && c != '\t')
5864098Seric 				break;
5874098Seric 
5884098Seric 			/* read continuation line */
5894098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
5904098Seric 				break;
5919368Seric 			LineNumber++;
59257135Seric 
59357135Seric 			/* check for line overflow */
59457135Seric 			if (strchr(p, '\n') == NULL)
59557135Seric 			{
59658151Seric 				usrerr("554 alias too long");
59757135Seric 				break;
59857135Seric 			}
5994098Seric 		}
60016898Seric 		if (al.q_mailer != LocalMailer)
60116898Seric 		{
60258151Seric 			syserr("554 cannot alias non-local names");
60316898Seric 			continue;
60416898Seric 		}
6054314Seric 
6064314Seric 		/*
6074314Seric 		**  Insert alias into symbol table or DBM file
6084314Seric 		*/
6094314Seric 
61057381Seric 		if (!bitnset(M_USR_UPPER, al.q_mailer->m_flags))
61157381Seric 			makelower(al.q_user);
6124322Seric 
61359673Seric 		lhssize = strlen(al.q_user);
61459673Seric 		rhssize = strlen(rhs);
615*60089Seric 		map->map_class->map_store(map, al.q_user, rhs);
6164157Seric 
61759673Seric 		if (al.q_paddr != NULL)
61859673Seric 			free(al.q_paddr);
61959673Seric 		if (al.q_host != NULL)
62059673Seric 			free(al.q_host);
62159673Seric 		if (al.q_user != NULL)
62259673Seric 			free(al.q_user);
6234322Seric 
6244322Seric 		/* statistics */
6254322Seric 		naliases++;
6264322Seric 		bytes += lhssize + rhssize;
6274322Seric 		if (rhssize > longest)
6284322Seric 			longest = rhssize;
6291515Seric 	}
63019784Seric 
63159673Seric 	e->e_to = NULL;
63259673Seric 	FileName = NULL;
63359733Seric 	if (Verbose || !automatic)
63459733Seric 		message("%s: %d aliases, longest %d bytes, %d bytes total",
635*60089Seric 			map->map_file, naliases, longest, bytes);
63659673Seric # ifdef LOG
63759673Seric 	if (LogLevel > 7)
63859673Seric 		syslog(LOG_INFO, "%s: %d aliases, longest %d bytes, %d bytes total",
639*60089Seric 			map->map_file, naliases, longest, bytes);
64059673Seric # endif /* LOG */
64159673Seric }
64259673Seric /*
643292Seric **  FORWARD -- Try to forward mail
644292Seric **
645292Seric **	This is similar but not identical to aliasing.
646292Seric **
647292Seric **	Parameters:
6484314Seric **		user -- the name of the user who's mail we would like
6494314Seric **			to forward to.  It must have been verified --
6504314Seric **			i.e., the q_home field must have been filled
6514314Seric **			in.
6524999Seric **		sendq -- a pointer to the head of the send queue to
6534999Seric **			put this user's aliases in.
654292Seric **
655292Seric **	Returns:
6564098Seric **		none.
657292Seric **
658292Seric **	Side Effects:
6593185Seric **		New names are added to send queues.
660292Seric */
661292Seric 
66255012Seric forward(user, sendq, e)
6632966Seric 	ADDRESS *user;
6644999Seric 	ADDRESS **sendq;
66555012Seric 	register ENVELOPE *e;
666292Seric {
66757136Seric 	char *pp;
66857136Seric 	char *ep;
6694069Seric 
6707671Seric 	if (tTd(27, 1))
6714098Seric 		printf("forward(%s)\n", user->q_paddr);
6724098Seric 
6734594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
6744098Seric 		return;
6754314Seric 	if (user->q_home == NULL)
67658059Seric 	{
67758151Seric 		syserr("554 forward: no home");
67858059Seric 		user->q_home = "/nosuchdirectory";
67958059Seric 	}
6804069Seric 
6814069Seric 	/* good address -- look for .forward file in home */
68255012Seric 	define('z', user->q_home, e);
68357136Seric 	define('u', user->q_user, e);
68457136Seric 	define('h', user->q_host, e);
68557136Seric 	if (ForwardPath == NULL)
68658050Seric 		ForwardPath = newstr("\201z/.forward");
68757136Seric 
68857136Seric 	for (pp = ForwardPath; pp != NULL; pp = ep)
68957136Seric 	{
69058247Seric 		int err;
69157232Seric 		char buf[MAXPATHLEN+1];
69257136Seric 
69357136Seric 		ep = strchr(pp, ':');
69457136Seric 		if (ep != NULL)
69557136Seric 			*ep = '\0';
69657136Seric 		expand(pp, buf, &buf[sizeof buf - 1], e);
69757136Seric 		if (ep != NULL)
69857136Seric 			*ep++ = ':';
69957136Seric 		if (tTd(27, 3))
70057136Seric 			printf("forward: trying %s\n", buf);
70158247Seric 		err = include(buf, TRUE, user, sendq, e);
70258247Seric 		if (err == 0)
70357136Seric 			break;
70458247Seric 		if (transienterror(err))
70558247Seric 		{
70658247Seric 			/* we have to suspend this message */
70759563Seric 			if (tTd(27, 2))
70859563Seric 				printf("forward: transient error on %s\n", buf);
70959563Seric #ifdef LOG
71059563Seric 			if (LogLevel > 2)
71159624Seric 				syslog(LOG_ERR, "%s: forward %s: transient error: %s",
71259624Seric 					e->e_id, buf, errstring(err));
71359563Seric #endif
71459611Seric 			message("%s: %s: message queued", buf, errstring(err));
71558247Seric 			user->q_flags |= QQUEUEUP|QDONTSEND;
71658247Seric 			return;
71758247Seric 		}
71857136Seric 	}
719292Seric }
720