1292Seric # include <pwd.h>
24212Seric # include <sys/types.h>
34212Seric # include <sys/stat.h>
43309Seric # include "sendmail.h"
5292Seric 
64106Seric # ifdef DBM
7*4999Seric static char SccsId[] = "@(#)alias.c	3.27	11/21/81	(with DBM)";
84106Seric # else DBM
9*4999Seric static char SccsId[] = "@(#)alias.c	3.27	11/21/81	(without DBM)";
104106Seric # endif DBM
11402Seric 
12292Seric /*
13292Seric **  ALIAS -- Compute aliases.
14292Seric **
154098Seric **	Scans the file /usr/lib/aliases for a set of aliases.
163185Seric **	If found, it arranges to deliver to them.  Uses libdbm
173185Seric **	database if -DDBM.
18292Seric **
19292Seric **	Parameters:
204097Seric **		a -- address to alias.
21*4999Seric **		sendq -- a pointer to the head of the send queue
22*4999Seric **			to put the aliases in.
23292Seric **
24292Seric **	Returns:
25292Seric **		none
26292Seric **
27292Seric **	Side Effects:
283185Seric **		Aliases found are expanded.
29292Seric **
30292Seric **	Files:
314098Seric **		/usr/lib/aliases -- the mail aliases.  The format is
32569Seric **			a series of lines of the form:
33569Seric **				alias:name1,name2,name3,...
34569Seric **			where 'alias' expands to all of
35569Seric **			'name[i]'.  Continuations begin with
36569Seric **			space or tab.
374098Seric **		/usr/lib/aliases.pag, /usr/lib/aliases.dir: libdbm version
381503Smark **			of alias file.  Keys are aliases, datums
391503Smark **			(data?) are name1,name2, ...
40292Seric **
41292Seric **	Notes:
42292Seric **		If NoAlias (the "-n" flag) is set, no aliasing is
43292Seric **			done.
44292Seric **
45292Seric **	Deficiencies:
46292Seric **		It should complain about names that are aliased to
47292Seric **			nothing.
48292Seric **		It is unsophisticated about line overflows.
49292Seric */
50292Seric 
51292Seric 
521503Smark #ifdef DBM
532966Seric typedef struct
542966Seric {
552966Seric 	char	*dptr;
564157Seric 	int	dsize;
574157Seric } DATUM;
584157Seric DATUM lhs, rhs;
594157Seric extern DATUM fetch();
601503Smark #endif DBM
61292Seric 
62*4999Seric alias(a, sendq)
634097Seric 	register ADDRESS *a;
64*4999Seric 	ADDRESS **sendq;
65292Seric {
664081Seric 	register char *p;
674081Seric # ifndef DBM
684098Seric 	register STAB *s;
694081Seric # endif DBM
70292Seric 
71292Seric 	if (NoAlias)
72292Seric 		return;
73292Seric # ifdef DEBUG
74292Seric 	if (Debug)
754098Seric 		printf("alias(%s)\n", a->q_paddr);
76292Seric # endif
77292Seric 
784098Seric 	/* don't realias already aliased names */
794098Seric 	if (bitset(QDONTSEND, a->q_flags))
804098Seric 		return;
814098Seric 
824098Seric 	To = a->q_paddr;
834098Seric 
844314Seric 	/*
854314Seric 	**  Look up this name
864314Seric 	*/
874314Seric 
884097Seric # ifdef DBM
894098Seric 	/* create a key for fetch */
904098Seric 	lhs.dptr = a->q_user;
914098Seric 	lhs.dsize = strlen(a->q_user) + 1;
924098Seric 	rhs = fetch(lhs);
934098Seric 
944098Seric 	/* find this alias? */
954098Seric 	p = rhs.dptr;
964098Seric 	if (p == NULL)
974098Seric 		return;
984098Seric # else DBM
994098Seric 	s = stab(a->q_user, ST_ALIAS, ST_FIND);
1004098Seric 	if (s == NULL)
1014098Seric 		return;
1024098Seric 	p = s->s_alias;
1034097Seric # endif DBM
104292Seric 
105292Seric 	/*
1064098Seric 	**  Match on Alias.
1074098Seric 	**	Deliver to the target list.
1081515Seric 	*/
1091515Seric 
1104098Seric # ifdef DEBUG
1114098Seric 	if (Debug)
1124098Seric 		printf("%s (%s, %s) aliased to %s\n",
1134098Seric 		    a->q_paddr, a->q_host, a->q_user, p);
1144098Seric # endif
1154098Seric 	if (Verbose)
1164165Seric 		message(Arpa_Info, "aliased to %s", p);
1174098Seric 	AliasLevel++;
118*4999Seric 	sendto(p, 1, a, sendq);
1194098Seric 	AliasLevel--;
1204098Seric }
1214098Seric /*
1224098Seric **  INITALIASES -- initialize for aliasing
1234098Seric **
1244098Seric **	Very different depending on whether we are running DBM or not.
1254098Seric **
1264098Seric **	Parameters:
1274098Seric **		aliasfile -- location of aliases.
1284157Seric **		init -- if set and if DBM, initialize the DBM files.
1294098Seric **
1304098Seric **	Returns:
1314098Seric **		none.
1324098Seric **
1334098Seric **	Side Effects:
1344098Seric **		initializes aliases:
1354098Seric **		if DBM:  opens the database.
1364098Seric **		if ~DBM: reads the aliases into the symbol table.
1374098Seric */
1384098Seric 
1394157Seric # define DBMMODE	0666
1404157Seric 
1414157Seric initaliases(aliasfile, init)
1424098Seric 	char *aliasfile;
1434157Seric 	bool init;
1444098Seric {
1454322Seric 	char buf[MAXNAME];
1464322Seric 	struct stat stb;
1474322Seric 	time_t modtime;
1484322Seric 
1494322Seric 	/*
1504322Seric 	**  See if the DBM version of the file is out of date with
1514322Seric 	**  the text version.  If so, go into 'init' mode automatically.
1524322Seric 	**	This only happens if our effective userid owns the DBM
1534325Seric 	**	version or if the mode of the database is 666 -- this
1544322Seric 	**	is an attempt to avoid protection problems.  Note the
1554322Seric 	**	unpalatable hack to see if the stat succeeded.
1564322Seric 	*/
1574322Seric 
1584322Seric 	if (stat(aliasfile, &stb) < 0)
1594322Seric 		return;
1604098Seric # ifdef DBM
1614322Seric 	modtime = stb.st_mtime;
1624322Seric 	(void) strcpy(buf, aliasfile);
1634322Seric 	(void) strcat(buf, ".pag");
1644322Seric 	stb.st_ino = 0;
1654322Seric 	if ((stat(buf, &stb) < 0 || stb.st_mtime < modtime) && !init)
1664322Seric 	{
1674322Seric 		if (stb.st_ino != 0 &&
1684325Seric 		    ((stb.st_mode & 0666) == 0666 || stb.st_uid == geteuid()))
1694322Seric 		{
1704322Seric 			init = TRUE;
1714322Seric 			if (Verbose)
1724322Seric 				message(Arpa_Info, "rebuilding alias database");
1734322Seric 		}
1744322Seric 		else
1754322Seric 		{
1764322Seric 			message(Arpa_Info, "Warning: alias database out of date");
1774322Seric 		}
1784322Seric 	}
1794322Seric # endif DBM
1804322Seric 
1814322Seric 	/*
1824322Seric 	**  If initializing, create the new files.
1834322Seric 	**	We should lock the alias file here to prevent other
1844322Seric 	**	instantiations of sendmail from reading an incomplete
1854322Seric 	**	file -- or worse yet, doing a concurrent initialize.
1864322Seric 	*/
1874322Seric 
1884322Seric # ifdef DBM
1894157Seric 	if (init)
1904157Seric 	{
1914157Seric 		(void) strcpy(buf, aliasfile);
1924157Seric 		(void) strcat(buf, ".dir");
1934157Seric 		if (close(creat(buf, DBMMODE)) < 0)
1944157Seric 		{
1954157Seric 			syserr("cannot make %s", buf);
1964157Seric 			return;
1974157Seric 		}
1984157Seric 		(void) strcpy(buf, aliasfile);
1994157Seric 		(void) strcat(buf, ".pag");
2004157Seric 		if (close(creat(buf, DBMMODE)) < 0)
2014157Seric 		{
2024157Seric 			syserr("cannot make %s", buf);
2034157Seric 			return;
2044157Seric 		}
2054157Seric 	}
2064322Seric 
2074322Seric 	/*
2084322Seric 	**  Open and, if necessary, load the DBM file.
2094322Seric 	**	If running without DBM, load the symbol table.
2104322Seric 	*/
2114322Seric 
2124098Seric 	dbminit(aliasfile);
2134157Seric 	if (init)
2144157Seric 		readaliases(aliasfile, TRUE);
2154098Seric # else DBM
2164157Seric 	readaliases(aliasfile, init);
2174157Seric # endif DBM
2184157Seric }
2194157Seric /*
2204157Seric **  READALIASES -- read and process the alias file.
2214157Seric **
2224157Seric **	This routine implements the part of initaliases that occurs
2234157Seric **	when we are not going to use the DBM stuff.
2244157Seric **
2254157Seric **	Parameters:
2264157Seric **		aliasfile -- the pathname of the alias file master.
2274157Seric **		init -- if set, initialize the DBM stuff.
2284157Seric **
2294157Seric **	Returns:
2304157Seric **		none.
2314157Seric **
2324157Seric **	Side Effects:
2334157Seric **		Reads aliasfile into the symbol table.
2344157Seric **		Optionally, builds the .dir & .pag files.
2354157Seric */
2364157Seric 
2374157Seric static
2384157Seric readaliases(aliasfile, init)
2394157Seric 	char *aliasfile;
2404157Seric 	bool init;
2414157Seric {
2424098Seric 	char line[BUFSIZ];
2434098Seric 	register char *p;
2444098Seric 	char *p2;
2454098Seric 	char *rhs;
2464098Seric 	bool skipping;
2474098Seric 	ADDRESS al, bl;
2484106Seric 	FILE *af;
2494106Seric 	int lineno;
2504106Seric 	register STAB *s;
2514322Seric 	int naliases, bytes, longest;
2524098Seric 
2534098Seric 	if ((af = fopen(aliasfile, "r")) == NULL)
2541515Seric 	{
2554098Seric # ifdef DEBUG
2564098Seric 		if (Debug)
2574106Seric 			printf("Can't open %s\n", aliasfile);
2584098Seric # endif
2594098Seric 		errno = 0;
2604098Seric 		NoAlias++;
2614098Seric 		return;
2624098Seric 	}
2634314Seric 
2644314Seric 	/*
2654314Seric 	**  Read and interpret lines
2664314Seric 	*/
2674314Seric 
2684098Seric 	lineno = 0;
2694322Seric 	naliases = bytes = longest = 0;
2704098Seric 	skipping = FALSE;
2714098Seric 	while (fgets(line, sizeof (line), af) != NULL)
2724098Seric 	{
2734322Seric 		int lhssize, rhssize;
2744322Seric 
2754098Seric 		lineno++;
2764098Seric 		switch (line[0])
2774098Seric 		{
2784098Seric 		  case '#':
2794098Seric 		  case '\n':
2804098Seric 		  case '\0':
2814098Seric 			skipping = FALSE;
2824098Seric 			continue;
2834065Seric 
2844098Seric 		  case ' ':
2854098Seric 		  case '\t':
2864098Seric 			if (!skipping)
2874098Seric 				syserr("aliases: %d: Non-continuation line starts with space", lineno);
2884098Seric 			skipping = TRUE;
2894097Seric 			continue;
2904098Seric 		}
2914098Seric 		skipping = FALSE;
2921874Seric 
2934314Seric 		/*
2944314Seric 		**  Process the LHS
2954314Seric 		**	Find the final colon, and parse the address.
2964314Seric 		**	It should resolve to a local name -- this will
2974314Seric 		**	be checked later (we want to optionally do
2984314Seric 		**	parsing of the RHS first to maximize error
2994314Seric 		**	detection).
3004314Seric 		*/
3014314Seric 
3024098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
3034097Seric 			continue;
3044098Seric 		if (*p == '\0' || *p == '\n')
3054098Seric 		{
3064098Seric 		 syntaxerr:
3074098Seric 			syserr("aliases: %d: missing colon", lineno);
3084097Seric 			continue;
3094098Seric 		}
3104098Seric 		*p++ = '\0';
3114098Seric 		if (parse(line, &al, 1) == NULL)
3124098Seric 		{
3134098Seric 			*--p = ':';
3144098Seric 			goto syntaxerr;
3154098Seric 		}
3164314Seric 
3174314Seric 		/*
3184314Seric 		**  Process the RHS.
3194314Seric 		**	'al' is the internal form of the LHS address.
3204314Seric 		**	'p' points to the text of the RHS.
3214314Seric 		*/
3224314Seric 
3234098Seric 		rhs = p;
3244098Seric 		for (;;)
3254098Seric 		{
3264098Seric 			register char c;
3271515Seric 
3284157Seric 			if (init)
3294098Seric 			{
3304157Seric 				/* do parsing & compression of addresses */
3314098Seric 				c = *p;
3324157Seric 				while (c != '\0')
3334098Seric 				{
3344157Seric 					p2 = p;
3354157Seric 					while (*p != '\n' && *p != ',' && *p != '\0')
3364157Seric 						p++;
3374157Seric 					c = *p;
3384157Seric 					*p++ = '\0';
3394322Seric 					if (c == '\n')
3404322Seric 						c = '\0';
3414157Seric 					if (*p2 == '\0')
3424157Seric 					{
3434157Seric 						p[-1] = c;
3444157Seric 						continue;
3454157Seric 					}
3464314Seric 					(void) parse(p2, &bl, -1);
3474098Seric 					p[-1] = c;
3484157Seric 					while (isspace(*p))
3494157Seric 						p++;
3504098Seric 				}
3514098Seric 			}
3524157Seric 			else
3534157Seric 				p = &p[strlen(p)];
3541515Seric 
3554098Seric 			/* see if there should be a continuation line */
3564106Seric 			c = fgetc(af);
3574106Seric 			if (!feof(af))
3584314Seric 				(void) ungetc(c, af);
3594106Seric 			if (c != ' ' && c != '\t')
3604098Seric 				break;
3614098Seric 
3624098Seric 			/* read continuation line */
3634098Seric 			p--;
3644098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
3654098Seric 				break;
3664098Seric 			lineno++;
3674098Seric 		}
3684594Seric 		if (al.q_mailer != LocalMailer)
3694098Seric 		{
3704098Seric 			syserr("aliases: %d: cannot alias non-local names", lineno);
3714098Seric 			continue;
3724098Seric 		}
3734314Seric 
3744314Seric 		/*
3754314Seric 		**  Insert alias into symbol table or DBM file
3764314Seric 		*/
3774314Seric 
3784322Seric 		lhssize = strlen(al.q_user) + 1;
3794322Seric 		rhssize = strlen(rhs) + 1;
3804322Seric 
3814157Seric # ifdef DBM
3824157Seric 		if (init)
3834157Seric 		{
3844157Seric 			DATUM key, content;
3854157Seric 
3864322Seric 			key.dsize = lhssize;
3874157Seric 			key.dptr = al.q_user;
3884322Seric 			content.dsize = rhssize;
3894157Seric 			content.dptr = rhs;
3904157Seric 			store(key, content);
3914157Seric 		}
3924157Seric 		else
3934157Seric # endif DBM
3944157Seric 		{
3954157Seric 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
3964157Seric 			s->s_alias = newstr(rhs);
3974157Seric 		}
3984322Seric 
3994322Seric 		/* statistics */
4004322Seric 		naliases++;
4014322Seric 		bytes += lhssize + rhssize;
4024322Seric 		if (rhssize > longest)
4034322Seric 			longest = rhssize;
4041515Seric 	}
4054098Seric 	(void) fclose(af);
4064322Seric 	To = NULL;
4074322Seric 	if (Verbose)
4084322Seric 		message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
4094322Seric 			naliases, longest, bytes);
410292Seric }
411292Seric /*
412292Seric **  FORWARD -- Try to forward mail
413292Seric **
414292Seric **	This is similar but not identical to aliasing.
415292Seric **
416292Seric **	Parameters:
4174314Seric **		user -- the name of the user who's mail we would like
4184314Seric **			to forward to.  It must have been verified --
4194314Seric **			i.e., the q_home field must have been filled
4204314Seric **			in.
421*4999Seric **		sendq -- a pointer to the head of the send queue to
422*4999Seric **			put this user's aliases in.
423292Seric **
424292Seric **	Returns:
4254098Seric **		none.
426292Seric **
427292Seric **	Side Effects:
4283185Seric **		New names are added to send queues.
429292Seric */
430292Seric 
431*4999Seric forward(user, sendq)
4322966Seric 	ADDRESS *user;
433*4999Seric 	ADDRESS **sendq;
434292Seric {
4354078Seric 	char buf[60];
4364536Seric 	extern bool safefile();
4374069Seric 
4384098Seric # ifdef DEBUG
4394098Seric 	if (Debug)
4404098Seric 		printf("forward(%s)\n", user->q_paddr);
4414098Seric # endif DEBUG
4424098Seric 
4434594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
4444098Seric 		return;
4454314Seric # ifdef DEBUG
4464314Seric 	if (user->q_home == NULL)
4474314Seric 		syserr("forward: no home");
4484314Seric # endif DEBUG
4494069Seric 
4504069Seric 	/* good address -- look for .forward file in home */
4514104Seric 	define('z', user->q_home);
4524081Seric 	(void) expand("$z/.forward", buf, &buf[sizeof buf - 1]);
4534536Seric 	if (!safefile(buf, user->q_uid, S_IREAD))
4544098Seric 		return;
4554069Seric 
4564069Seric 	/* we do have an address to forward to -- do it */
457*4999Seric 	include(buf, "forwarding", user, sendq);
458292Seric }
459