xref: /freebsd/contrib/sendmail/src/alias.c (revision 94c01205742119acdf56dbbd22fc274e5e38cfd4)
1c2aa98e2SPeter Wemm /*
294c01205SGregory Neil Shapiro  * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
306f25ae9SGregory Neil Shapiro  *	All rights reserved.
4c2aa98e2SPeter Wemm  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
506f25ae9SGregory Neil Shapiro  * Copyright (c) 1988, 1993
606f25ae9SGregory Neil Shapiro  *	The Regents of the University of California.  All rights reserved.
7c2aa98e2SPeter Wemm  *
8c2aa98e2SPeter Wemm  * By using this file, you agree to the terms and conditions set
9c2aa98e2SPeter Wemm  * forth in the LICENSE file which can be found at the top level of
10c2aa98e2SPeter Wemm  * the sendmail distribution.
1140266059SGregory Neil Shapiro  *
12c2aa98e2SPeter Wemm  */
13c2aa98e2SPeter Wemm 
1406f25ae9SGregory Neil Shapiro #include <sendmail.h>
15c2aa98e2SPeter Wemm 
1694c01205SGregory Neil Shapiro SM_RCSID("@(#)$Id: alias.c,v 8.214 2002/05/24 20:50:16 gshapiro Exp $")
17c2aa98e2SPeter Wemm 
1806f25ae9SGregory Neil Shapiro #define SEPARATOR ':'
1906f25ae9SGregory Neil Shapiro # define ALIAS_SPEC_SEPARATORS	" ,/:"
20c2aa98e2SPeter Wemm 
2106f25ae9SGregory Neil Shapiro static MAP	*AliasFileMap = NULL;	/* the actual aliases.files map */
2206f25ae9SGregory Neil Shapiro static int	NAliasFileMaps;	/* the number of entries in AliasFileMap */
2306f25ae9SGregory Neil Shapiro 
2440266059SGregory Neil Shapiro static char	*aliaslookup __P((char *, int *, char *));
2506f25ae9SGregory Neil Shapiro 
2640266059SGregory Neil Shapiro /*
27c2aa98e2SPeter Wemm **  ALIAS -- Compute aliases.
28c2aa98e2SPeter Wemm **
29c2aa98e2SPeter Wemm **	Scans the alias file for an alias for the given address.
30c2aa98e2SPeter Wemm **	If found, it arranges to deliver to the alias list instead.
31c2aa98e2SPeter Wemm **	Uses libdbm database if -DDBM.
32c2aa98e2SPeter Wemm **
33c2aa98e2SPeter Wemm **	Parameters:
34c2aa98e2SPeter Wemm **		a -- address to alias.
35c2aa98e2SPeter Wemm **		sendq -- a pointer to the head of the send queue
36c2aa98e2SPeter Wemm **			to put the aliases in.
37c2aa98e2SPeter Wemm **		aliaslevel -- the current alias nesting depth.
38c2aa98e2SPeter Wemm **		e -- the current envelope.
39c2aa98e2SPeter Wemm **
40c2aa98e2SPeter Wemm **	Returns:
41c2aa98e2SPeter Wemm **		none
42c2aa98e2SPeter Wemm **
43c2aa98e2SPeter Wemm **	Side Effects:
44c2aa98e2SPeter Wemm **		Aliases found are expanded.
45c2aa98e2SPeter Wemm **
46c2aa98e2SPeter Wemm **	Deficiencies:
47c2aa98e2SPeter Wemm **		It should complain about names that are aliased to
48c2aa98e2SPeter Wemm **			nothing.
49c2aa98e2SPeter Wemm */
50c2aa98e2SPeter Wemm 
51c2aa98e2SPeter Wemm void
52c2aa98e2SPeter Wemm alias(a, sendq, aliaslevel, e)
53c2aa98e2SPeter Wemm 	register ADDRESS *a;
54c2aa98e2SPeter Wemm 	ADDRESS **sendq;
55c2aa98e2SPeter Wemm 	int aliaslevel;
56c2aa98e2SPeter Wemm 	register ENVELOPE *e;
57c2aa98e2SPeter Wemm {
58c2aa98e2SPeter Wemm 	register char *p;
59c2aa98e2SPeter Wemm 	char *owner;
6006f25ae9SGregory Neil Shapiro 	auto int status = EX_OK;
61c2aa98e2SPeter Wemm 	char obuf[MAXNAME + 7];
62c2aa98e2SPeter Wemm 
63c2aa98e2SPeter Wemm 	if (tTd(27, 1))
6440266059SGregory Neil Shapiro 		sm_dprintf("alias(%s)\n", a->q_user);
65c2aa98e2SPeter Wemm 
66c2aa98e2SPeter Wemm 	/* don't realias already aliased names */
6706f25ae9SGregory Neil Shapiro 	if (!QS_IS_OK(a->q_state))
68c2aa98e2SPeter Wemm 		return;
69c2aa98e2SPeter Wemm 
70c2aa98e2SPeter Wemm 	if (NoAlias)
71c2aa98e2SPeter Wemm 		return;
72c2aa98e2SPeter Wemm 
73c2aa98e2SPeter Wemm 	e->e_to = a->q_paddr;
74c2aa98e2SPeter Wemm 
75c2aa98e2SPeter Wemm 	/*
76c2aa98e2SPeter Wemm 	**  Look up this name.
77c2aa98e2SPeter Wemm 	**
78c2aa98e2SPeter Wemm 	**	If the map was unavailable, we will queue this message
79c2aa98e2SPeter Wemm 	**	until the map becomes available; otherwise, we could
80c2aa98e2SPeter Wemm 	**	bounce messages inappropriately.
81c2aa98e2SPeter Wemm 	*/
82c2aa98e2SPeter Wemm 
8306f25ae9SGregory Neil Shapiro #if _FFR_REDIRECTEMPTY
8406f25ae9SGregory Neil Shapiro 	/*
8506f25ae9SGregory Neil Shapiro 	**  envelope <> can't be sent to mailing lists, only owner-
8606f25ae9SGregory Neil Shapiro 	**  send spam of this type to owner- of the list
8706f25ae9SGregory Neil Shapiro 	**  ----  to stop spam from going to mailing lists!
8806f25ae9SGregory Neil Shapiro 	*/
8940266059SGregory Neil Shapiro 
9006f25ae9SGregory Neil Shapiro 	if (e->e_sender != NULL && *e->e_sender == '\0')
91c2aa98e2SPeter Wemm 	{
9206f25ae9SGregory Neil Shapiro 		/* Look for owner of alias */
9340266059SGregory Neil Shapiro 		(void) sm_strlcpyn(obuf, sizeof obuf, 2, "owner-", a->q_user);
9440266059SGregory Neil Shapiro 		if (aliaslookup(obuf, &status, a->q_host) != NULL)
9506f25ae9SGregory Neil Shapiro 		{
9606f25ae9SGregory Neil Shapiro 			if (LogLevel > 8)
9706f25ae9SGregory Neil Shapiro 				syslog(LOG_WARNING,
9806f25ae9SGregory Neil Shapiro 				       "possible spam from <> to list: %s, redirected to %s\n",
9906f25ae9SGregory Neil Shapiro 				       a->q_user, obuf);
10040266059SGregory Neil Shapiro 			a->q_user = sm_rpool_strdup_x(e->e_rpool, obuf);
10106f25ae9SGregory Neil Shapiro 		}
10206f25ae9SGregory Neil Shapiro 	}
10306f25ae9SGregory Neil Shapiro #endif /* _FFR_REDIRECTEMPTY */
10406f25ae9SGregory Neil Shapiro 
10540266059SGregory Neil Shapiro 	p = aliaslookup(a->q_user, &status, a->q_host);
10606f25ae9SGregory Neil Shapiro 	if (status == EX_TEMPFAIL || status == EX_UNAVAILABLE)
10706f25ae9SGregory Neil Shapiro 	{
10806f25ae9SGregory Neil Shapiro 		a->q_state = QS_QUEUEUP;
109c2aa98e2SPeter Wemm 		if (e->e_message == NULL)
11040266059SGregory Neil Shapiro 			e->e_message = "alias database unavailable";
11140266059SGregory Neil Shapiro 
11240266059SGregory Neil Shapiro 		/* XXX msg only per recipient? */
11340266059SGregory Neil Shapiro 		if (a->q_message == NULL)
11440266059SGregory Neil Shapiro 			a->q_message = "alias database unavailable";
115c2aa98e2SPeter Wemm 		return;
116c2aa98e2SPeter Wemm 	}
117c2aa98e2SPeter Wemm 	if (p == NULL)
118c2aa98e2SPeter Wemm 		return;
119c2aa98e2SPeter Wemm 
120c2aa98e2SPeter Wemm 	/*
121c2aa98e2SPeter Wemm 	**  Match on Alias.
122c2aa98e2SPeter Wemm 	**	Deliver to the target list.
123c2aa98e2SPeter Wemm 	*/
124c2aa98e2SPeter Wemm 
125c2aa98e2SPeter Wemm 	if (tTd(27, 1))
12640266059SGregory Neil Shapiro 		sm_dprintf("%s (%s, %s) aliased to %s\n",
127c2aa98e2SPeter Wemm 			   a->q_paddr, a->q_host, a->q_user, p);
128c2aa98e2SPeter Wemm 	if (bitset(EF_VRFYONLY, e->e_flags))
129c2aa98e2SPeter Wemm 	{
13006f25ae9SGregory Neil Shapiro 		a->q_state = QS_VERIFIED;
131c2aa98e2SPeter Wemm 		return;
132c2aa98e2SPeter Wemm 	}
133c2aa98e2SPeter Wemm 	message("aliased to %s", shortenstring(p, MAXSHORTSTR));
13406f25ae9SGregory Neil Shapiro 	if (LogLevel > 10)
135c2aa98e2SPeter Wemm 		sm_syslog(LOG_INFO, e->e_id,
136c2aa98e2SPeter Wemm 			  "alias %.100s => %s",
137c2aa98e2SPeter Wemm 			  a->q_paddr, shortenstring(p, MAXSHORTSTR));
138c2aa98e2SPeter Wemm 	a->q_flags &= ~QSELFREF;
139c2aa98e2SPeter Wemm 	if (tTd(27, 5))
140c2aa98e2SPeter Wemm 	{
14140266059SGregory Neil Shapiro 		sm_dprintf("alias: QS_EXPANDED ");
14240266059SGregory Neil Shapiro 		printaddr(a, false);
143c2aa98e2SPeter Wemm 	}
14406f25ae9SGregory Neil Shapiro 	a->q_state = QS_EXPANDED;
14506f25ae9SGregory Neil Shapiro 
14606f25ae9SGregory Neil Shapiro 	/*
14706f25ae9SGregory Neil Shapiro 	**  Always deliver aliased items as the default user.
14806f25ae9SGregory Neil Shapiro 	**  Setting q_gid to 0 forces deliver() to use DefUser
14906f25ae9SGregory Neil Shapiro 	**  instead of the alias name for the call to initgroups().
15006f25ae9SGregory Neil Shapiro 	*/
15106f25ae9SGregory Neil Shapiro 
15206f25ae9SGregory Neil Shapiro 	a->q_uid = DefUid;
15306f25ae9SGregory Neil Shapiro 	a->q_gid = 0;
15406f25ae9SGregory Neil Shapiro 	a->q_fullname = NULL;
15506f25ae9SGregory Neil Shapiro 	a->q_flags |= QGOODUID|QALIAS;
15606f25ae9SGregory Neil Shapiro 
157c2aa98e2SPeter Wemm 	(void) sendtolist(p, a, sendq, aliaslevel + 1, e);
15840266059SGregory Neil Shapiro 
15906f25ae9SGregory Neil Shapiro 	if (bitset(QSELFREF, a->q_flags) && QS_IS_EXPANDED(a->q_state))
16006f25ae9SGregory Neil Shapiro 		a->q_state = QS_OK;
161c2aa98e2SPeter Wemm 
162c2aa98e2SPeter Wemm 	/*
163c2aa98e2SPeter Wemm 	**  Look for owner of alias
164c2aa98e2SPeter Wemm 	*/
165c2aa98e2SPeter Wemm 
166c2aa98e2SPeter Wemm 	if (strncmp(a->q_user, "owner-", 6) == 0 ||
16740266059SGregory Neil Shapiro 	    strlen(a->q_user) > sizeof obuf - 7)
16840266059SGregory Neil Shapiro 		(void) sm_strlcpy(obuf, "owner-owner", sizeof obuf);
169c2aa98e2SPeter Wemm 	else
17040266059SGregory Neil Shapiro 		(void) sm_strlcpyn(obuf, sizeof obuf, 2, "owner-", a->q_user);
17140266059SGregory Neil Shapiro 	owner = aliaslookup(obuf, &status, a->q_host);
172c2aa98e2SPeter Wemm 	if (owner == NULL)
173c2aa98e2SPeter Wemm 		return;
174c2aa98e2SPeter Wemm 
175c2aa98e2SPeter Wemm 	/* reflect owner into envelope sender */
176c2aa98e2SPeter Wemm 	if (strpbrk(owner, ",:/|\"") != NULL)
177c2aa98e2SPeter Wemm 		owner = obuf;
17840266059SGregory Neil Shapiro 	a->q_owner = sm_rpool_strdup_x(e->e_rpool, owner);
179c2aa98e2SPeter Wemm 
180c2aa98e2SPeter Wemm 	/* announce delivery to this alias; NORECEIPT bit set later */
181c2aa98e2SPeter Wemm 	if (e->e_xfp != NULL)
18240266059SGregory Neil Shapiro 		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
18340266059SGregory Neil Shapiro 				"Message delivered to mailing list %s\n",
184c2aa98e2SPeter Wemm 				a->q_paddr);
185c2aa98e2SPeter Wemm 	e->e_flags |= EF_SENDRECEIPT;
186c2aa98e2SPeter Wemm 	a->q_flags |= QDELIVERED|QEXPANDED;
187c2aa98e2SPeter Wemm }
18840266059SGregory Neil Shapiro /*
189c2aa98e2SPeter Wemm **  ALIASLOOKUP -- look up a name in the alias file.
190c2aa98e2SPeter Wemm **
191c2aa98e2SPeter Wemm **	Parameters:
192c2aa98e2SPeter Wemm **		name -- the name to look up.
193c2aa98e2SPeter Wemm **		pstat -- a pointer to a place to put the status.
19440266059SGregory Neil Shapiro **		av -- argument for %1 expansion.
195c2aa98e2SPeter Wemm **
196c2aa98e2SPeter Wemm **	Returns:
197c2aa98e2SPeter Wemm **		the value of name.
198c2aa98e2SPeter Wemm **		NULL if unknown.
199c2aa98e2SPeter Wemm **
200c2aa98e2SPeter Wemm **	Side Effects:
201c2aa98e2SPeter Wemm **		none.
202c2aa98e2SPeter Wemm **
203c2aa98e2SPeter Wemm **	Warnings:
204c2aa98e2SPeter Wemm **		The return value will be trashed across calls.
205c2aa98e2SPeter Wemm */
206c2aa98e2SPeter Wemm 
20706f25ae9SGregory Neil Shapiro static char *
20840266059SGregory Neil Shapiro aliaslookup(name, pstat, av)
209c2aa98e2SPeter Wemm 	char *name;
210c2aa98e2SPeter Wemm 	int *pstat;
21140266059SGregory Neil Shapiro 	char *av;
212c2aa98e2SPeter Wemm {
213c2aa98e2SPeter Wemm 	static MAP *map = NULL;
21440266059SGregory Neil Shapiro #if _FFR_ALIAS_DETAIL
21540266059SGregory Neil Shapiro 	int i;
21640266059SGregory Neil Shapiro 	char *argv[4];
21740266059SGregory Neil Shapiro #endif /* _FFR_ALIAS_DETAIL */
218c2aa98e2SPeter Wemm 
219c2aa98e2SPeter Wemm 	if (map == NULL)
220c2aa98e2SPeter Wemm 	{
221c2aa98e2SPeter Wemm 		STAB *s = stab("aliases", ST_MAP, ST_FIND);
222c2aa98e2SPeter Wemm 
223c2aa98e2SPeter Wemm 		if (s == NULL)
224c2aa98e2SPeter Wemm 			return NULL;
225c2aa98e2SPeter Wemm 		map = &s->s_map;
226c2aa98e2SPeter Wemm 	}
22706f25ae9SGregory Neil Shapiro 	DYNOPENMAP(map);
228c2aa98e2SPeter Wemm 
229c2aa98e2SPeter Wemm 	/* special case POstMastER -- always use lower case */
23040266059SGregory Neil Shapiro 	if (sm_strcasecmp(name, "postmaster") == 0)
231c2aa98e2SPeter Wemm 		name = "postmaster";
232c2aa98e2SPeter Wemm 
23340266059SGregory Neil Shapiro #if _FFR_ALIAS_DETAIL
23440266059SGregory Neil Shapiro 	i = 0;
23540266059SGregory Neil Shapiro 	argv[i++] = name;
23640266059SGregory Neil Shapiro 	argv[i++] = av;
23740266059SGregory Neil Shapiro 
23840266059SGregory Neil Shapiro 	/* XXX '+' is hardwired here as delimiter! */
23940266059SGregory Neil Shapiro 	if (av != NULL && *av == '+')
24040266059SGregory Neil Shapiro 		argv[i++] = av + 1;
24140266059SGregory Neil Shapiro 	argv[i++] = NULL;
24240266059SGregory Neil Shapiro 	return (*map->map_class->map_lookup)(map, name, argv, pstat);
24340266059SGregory Neil Shapiro #else /* _FFR_ALIAS_DETAIL */
244c2aa98e2SPeter Wemm 	return (*map->map_class->map_lookup)(map, name, NULL, pstat);
24540266059SGregory Neil Shapiro #endif /* _FFR_ALIAS_DETAIL */
246c2aa98e2SPeter Wemm }
24740266059SGregory Neil Shapiro /*
248c2aa98e2SPeter Wemm **  SETALIAS -- set up an alias map
249c2aa98e2SPeter Wemm **
250c2aa98e2SPeter Wemm **	Called when reading configuration file.
251c2aa98e2SPeter Wemm **
252c2aa98e2SPeter Wemm **	Parameters:
253c2aa98e2SPeter Wemm **		spec -- the alias specification
254c2aa98e2SPeter Wemm **
255c2aa98e2SPeter Wemm **	Returns:
256c2aa98e2SPeter Wemm **		none.
257c2aa98e2SPeter Wemm */
258c2aa98e2SPeter Wemm 
259c2aa98e2SPeter Wemm void
260c2aa98e2SPeter Wemm setalias(spec)
261c2aa98e2SPeter Wemm 	char *spec;
262c2aa98e2SPeter Wemm {
263c2aa98e2SPeter Wemm 	register char *p;
264c2aa98e2SPeter Wemm 	register MAP *map;
265c2aa98e2SPeter Wemm 	char *class;
266c2aa98e2SPeter Wemm 	STAB *s;
267c2aa98e2SPeter Wemm 
268c2aa98e2SPeter Wemm 	if (tTd(27, 8))
26940266059SGregory Neil Shapiro 		sm_dprintf("setalias(%s)\n", spec);
270c2aa98e2SPeter Wemm 
271c2aa98e2SPeter Wemm 	for (p = spec; p != NULL; )
272c2aa98e2SPeter Wemm 	{
273c2aa98e2SPeter Wemm 		char buf[50];
274c2aa98e2SPeter Wemm 
275c2aa98e2SPeter Wemm 		while (isascii(*p) && isspace(*p))
276c2aa98e2SPeter Wemm 			p++;
277c2aa98e2SPeter Wemm 		if (*p == '\0')
278c2aa98e2SPeter Wemm 			break;
279c2aa98e2SPeter Wemm 		spec = p;
280c2aa98e2SPeter Wemm 
281c2aa98e2SPeter Wemm 		if (NAliasFileMaps >= MAXMAPSTACK)
282c2aa98e2SPeter Wemm 		{
283c2aa98e2SPeter Wemm 			syserr("Too many alias databases defined, %d max",
284c2aa98e2SPeter Wemm 				MAXMAPSTACK);
285c2aa98e2SPeter Wemm 			return;
286c2aa98e2SPeter Wemm 		}
287c2aa98e2SPeter Wemm 		if (AliasFileMap == NULL)
288c2aa98e2SPeter Wemm 		{
28940266059SGregory Neil Shapiro 			(void) sm_strlcpy(buf, "aliases.files sequence",
29006f25ae9SGregory Neil Shapiro 					  sizeof buf);
291c2aa98e2SPeter Wemm 			AliasFileMap = makemapentry(buf);
292c2aa98e2SPeter Wemm 			if (AliasFileMap == NULL)
293c2aa98e2SPeter Wemm 			{
294c2aa98e2SPeter Wemm 				syserr("setalias: cannot create aliases.files map");
295c2aa98e2SPeter Wemm 				return;
296c2aa98e2SPeter Wemm 			}
297c2aa98e2SPeter Wemm 		}
29840266059SGregory Neil Shapiro 		(void) sm_snprintf(buf, sizeof buf, "Alias%d", NAliasFileMaps);
299c2aa98e2SPeter Wemm 		s = stab(buf, ST_MAP, ST_ENTER);
300c2aa98e2SPeter Wemm 		map = &s->s_map;
30106f25ae9SGregory Neil Shapiro 		memset(map, '\0', sizeof *map);
302c2aa98e2SPeter Wemm 		map->map_mname = s->s_name;
30306f25ae9SGregory Neil Shapiro 		p = strpbrk(p, ALIAS_SPEC_SEPARATORS);
30406f25ae9SGregory Neil Shapiro 		if (p != NULL && *p == SEPARATOR)
305c2aa98e2SPeter Wemm 		{
306c2aa98e2SPeter Wemm 			/* map name */
307c2aa98e2SPeter Wemm 			*p++ = '\0';
308c2aa98e2SPeter Wemm 			class = spec;
309c2aa98e2SPeter Wemm 			spec = p;
310c2aa98e2SPeter Wemm 		}
311c2aa98e2SPeter Wemm 		else
312c2aa98e2SPeter Wemm 		{
313c2aa98e2SPeter Wemm 			class = "implicit";
314c2aa98e2SPeter Wemm 			map->map_mflags = MF_INCLNULL;
315c2aa98e2SPeter Wemm 		}
316c2aa98e2SPeter Wemm 
317c2aa98e2SPeter Wemm 		/* find end of spec */
318c2aa98e2SPeter Wemm 		if (p != NULL)
31906f25ae9SGregory Neil Shapiro 		{
32040266059SGregory Neil Shapiro 			bool quoted = false;
32106f25ae9SGregory Neil Shapiro 
32206f25ae9SGregory Neil Shapiro 			for (; *p != '\0'; p++)
32306f25ae9SGregory Neil Shapiro 			{
32406f25ae9SGregory Neil Shapiro 				/*
32506f25ae9SGregory Neil Shapiro 				**  Don't break into a quoted string.
32606f25ae9SGregory Neil Shapiro 				**  Needed for ldap maps which use
32706f25ae9SGregory Neil Shapiro 				**  commas in their specifications.
32806f25ae9SGregory Neil Shapiro 				*/
32906f25ae9SGregory Neil Shapiro 
33006f25ae9SGregory Neil Shapiro 				if (*p == '"')
33106f25ae9SGregory Neil Shapiro 					quoted = !quoted;
33206f25ae9SGregory Neil Shapiro 				else if (*p == ',' && !quoted)
33306f25ae9SGregory Neil Shapiro 					break;
33406f25ae9SGregory Neil Shapiro 			}
33506f25ae9SGregory Neil Shapiro 
33606f25ae9SGregory Neil Shapiro 			/* No more alias specifications follow */
33706f25ae9SGregory Neil Shapiro 			if (*p == '\0')
33806f25ae9SGregory Neil Shapiro 				p = NULL;
33906f25ae9SGregory Neil Shapiro 		}
340c2aa98e2SPeter Wemm 		if (p != NULL)
341c2aa98e2SPeter Wemm 			*p++ = '\0';
342c2aa98e2SPeter Wemm 
343c2aa98e2SPeter Wemm 		if (tTd(27, 20))
34440266059SGregory Neil Shapiro 			sm_dprintf("  map %s:%s %s\n", class, s->s_name, spec);
345c2aa98e2SPeter Wemm 
346c2aa98e2SPeter Wemm 		/* look up class */
347c2aa98e2SPeter Wemm 		s = stab(class, ST_MAPCLASS, ST_FIND);
348c2aa98e2SPeter Wemm 		if (s == NULL)
349c2aa98e2SPeter Wemm 		{
350c2aa98e2SPeter Wemm 			syserr("setalias: unknown alias class %s", class);
351c2aa98e2SPeter Wemm 		}
352c2aa98e2SPeter Wemm 		else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
353c2aa98e2SPeter Wemm 		{
354c2aa98e2SPeter Wemm 			syserr("setalias: map class %s can't handle aliases",
355c2aa98e2SPeter Wemm 				class);
356c2aa98e2SPeter Wemm 		}
357c2aa98e2SPeter Wemm 		else
358c2aa98e2SPeter Wemm 		{
359c2aa98e2SPeter Wemm 			map->map_class = &s->s_mapclass;
36040266059SGregory Neil Shapiro 			map->map_mflags |= MF_ALIAS;
361c2aa98e2SPeter Wemm 			if (map->map_class->map_parse(map, spec))
362c2aa98e2SPeter Wemm 			{
36340266059SGregory Neil Shapiro 				map->map_mflags |= MF_VALID;
364c2aa98e2SPeter Wemm 				AliasFileMap->map_stack[NAliasFileMaps++] = map;
365c2aa98e2SPeter Wemm 			}
366c2aa98e2SPeter Wemm 		}
367c2aa98e2SPeter Wemm 	}
368c2aa98e2SPeter Wemm }
36940266059SGregory Neil Shapiro /*
370c2aa98e2SPeter Wemm **  ALIASWAIT -- wait for distinguished @:@ token to appear.
371c2aa98e2SPeter Wemm **
372c2aa98e2SPeter Wemm **	This can decide to reopen or rebuild the alias file
373c2aa98e2SPeter Wemm **
374c2aa98e2SPeter Wemm **	Parameters:
375c2aa98e2SPeter Wemm **		map -- a pointer to the map descriptor for this alias file.
376c2aa98e2SPeter Wemm **		ext -- the filename extension (e.g., ".db") for the
377c2aa98e2SPeter Wemm **			database file.
378c2aa98e2SPeter Wemm **		isopen -- if set, the database is already open, and we
379c2aa98e2SPeter Wemm **			should check for validity; otherwise, we are
380c2aa98e2SPeter Wemm **			just checking to see if it should be created.
381c2aa98e2SPeter Wemm **
382c2aa98e2SPeter Wemm **	Returns:
38340266059SGregory Neil Shapiro **		true -- if the database is open when we return.
38440266059SGregory Neil Shapiro **		false -- if the database is closed when we return.
385c2aa98e2SPeter Wemm */
386c2aa98e2SPeter Wemm 
387c2aa98e2SPeter Wemm bool
388c2aa98e2SPeter Wemm aliaswait(map, ext, isopen)
389c2aa98e2SPeter Wemm 	MAP *map;
390c2aa98e2SPeter Wemm 	char *ext;
39106f25ae9SGregory Neil Shapiro 	bool isopen;
392c2aa98e2SPeter Wemm {
39340266059SGregory Neil Shapiro 	bool attimeout = false;
394c2aa98e2SPeter Wemm 	time_t mtime;
395c2aa98e2SPeter Wemm 	struct stat stb;
39694c01205SGregory Neil Shapiro 	char buf[MAXPATHLEN];
397c2aa98e2SPeter Wemm 
398c2aa98e2SPeter Wemm 	if (tTd(27, 3))
39940266059SGregory Neil Shapiro 		sm_dprintf("aliaswait(%s:%s)\n",
400c2aa98e2SPeter Wemm 			   map->map_class->map_cname, map->map_file);
401c2aa98e2SPeter Wemm 	if (bitset(MF_ALIASWAIT, map->map_mflags))
402c2aa98e2SPeter Wemm 		return isopen;
403c2aa98e2SPeter Wemm 	map->map_mflags |= MF_ALIASWAIT;
404c2aa98e2SPeter Wemm 
405c2aa98e2SPeter Wemm 	if (SafeAlias > 0)
406c2aa98e2SPeter Wemm 	{
407c2aa98e2SPeter Wemm 		auto int st;
408c2aa98e2SPeter Wemm 		unsigned int sleeptime = 2;
40940266059SGregory Neil Shapiro 		unsigned int loopcount = 0;	/* only used for debugging */
41040266059SGregory Neil Shapiro 		time_t toolong = curtime() + SafeAlias;
411c2aa98e2SPeter Wemm 
412c2aa98e2SPeter Wemm 		while (isopen &&
413c2aa98e2SPeter Wemm 		       map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
414c2aa98e2SPeter Wemm 		{
415c2aa98e2SPeter Wemm 			if (curtime() > toolong)
416c2aa98e2SPeter Wemm 			{
417c2aa98e2SPeter Wemm 				/* we timed out */
41840266059SGregory Neil Shapiro 				attimeout = true;
419c2aa98e2SPeter Wemm 				break;
420c2aa98e2SPeter Wemm 			}
421c2aa98e2SPeter Wemm 
422c2aa98e2SPeter Wemm 			/*
423c2aa98e2SPeter Wemm 			**  Close and re-open the alias database in case
424c2aa98e2SPeter Wemm 			**  the one is mv'ed instead of cp'ed in.
425c2aa98e2SPeter Wemm 			*/
426c2aa98e2SPeter Wemm 
427c2aa98e2SPeter Wemm 			if (tTd(27, 2))
42840266059SGregory Neil Shapiro 			{
42940266059SGregory Neil Shapiro 				loopcount++;
43040266059SGregory Neil Shapiro 				sm_dprintf("aliaswait: sleeping for %u seconds (loopcount = %u)\n",
43140266059SGregory Neil Shapiro 					   sleeptime, loopcount);
43240266059SGregory Neil Shapiro 			}
433c2aa98e2SPeter Wemm 
4348774250cSGregory Neil Shapiro 			map->map_mflags |= MF_CLOSING;
435c2aa98e2SPeter Wemm 			map->map_class->map_close(map);
4368774250cSGregory Neil Shapiro 			map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
43706f25ae9SGregory Neil Shapiro 			(void) sleep(sleeptime);
438c2aa98e2SPeter Wemm 			sleeptime *= 2;
439c2aa98e2SPeter Wemm 			if (sleeptime > 60)
440c2aa98e2SPeter Wemm 				sleeptime = 60;
441c2aa98e2SPeter Wemm 			isopen = map->map_class->map_open(map, O_RDONLY);
442c2aa98e2SPeter Wemm 		}
443c2aa98e2SPeter Wemm 	}
444c2aa98e2SPeter Wemm 
445c2aa98e2SPeter Wemm 	/* see if we need to go into auto-rebuild mode */
446c2aa98e2SPeter Wemm 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
447c2aa98e2SPeter Wemm 	{
448c2aa98e2SPeter Wemm 		if (tTd(27, 3))
44940266059SGregory Neil Shapiro 			sm_dprintf("aliaswait: not rebuildable\n");
450c2aa98e2SPeter Wemm 		map->map_mflags &= ~MF_ALIASWAIT;
451c2aa98e2SPeter Wemm 		return isopen;
452c2aa98e2SPeter Wemm 	}
453c2aa98e2SPeter Wemm 	if (stat(map->map_file, &stb) < 0)
454c2aa98e2SPeter Wemm 	{
455c2aa98e2SPeter Wemm 		if (tTd(27, 3))
45640266059SGregory Neil Shapiro 			sm_dprintf("aliaswait: no source file\n");
457c2aa98e2SPeter Wemm 		map->map_mflags &= ~MF_ALIASWAIT;
458c2aa98e2SPeter Wemm 		return isopen;
459c2aa98e2SPeter Wemm 	}
460c2aa98e2SPeter Wemm 	mtime = stb.st_mtime;
46194c01205SGregory Neil Shapiro 	if (sm_strlcpyn(buf, sizeof buf, 2,
46294c01205SGregory Neil Shapiro 			map->map_file, ext == NULL ? "" : ext) >= sizeof buf)
46394c01205SGregory Neil Shapiro 	{
46494c01205SGregory Neil Shapiro 		if (LogLevel > 3)
46594c01205SGregory Neil Shapiro 			sm_syslog(LOG_INFO, NOQID,
46694c01205SGregory Neil Shapiro 				  "alias database %s%s name too long",
467c2aa98e2SPeter Wemm 				  map->map_file, ext == NULL ? "" : ext);
46894c01205SGregory Neil Shapiro 		message("alias database %s%s name too long",
46994c01205SGregory Neil Shapiro 			map->map_file, ext == NULL ? "" : ext);
47094c01205SGregory Neil Shapiro 	}
47194c01205SGregory Neil Shapiro 
472c2aa98e2SPeter Wemm 	if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout)
473c2aa98e2SPeter Wemm 	{
474c2aa98e2SPeter Wemm 		if (LogLevel > 3)
475c2aa98e2SPeter Wemm 			sm_syslog(LOG_INFO, NOQID,
47640266059SGregory Neil Shapiro 				  "alias database %s out of date", buf);
477c2aa98e2SPeter Wemm 		message("Warning: alias database %s out of date", buf);
478c2aa98e2SPeter Wemm 	}
479c2aa98e2SPeter Wemm 	map->map_mflags &= ~MF_ALIASWAIT;
480c2aa98e2SPeter Wemm 	return isopen;
481c2aa98e2SPeter Wemm }
48240266059SGregory Neil Shapiro /*
483c2aa98e2SPeter Wemm **  REBUILDALIASES -- rebuild the alias database.
484c2aa98e2SPeter Wemm **
485c2aa98e2SPeter Wemm **	Parameters:
486c2aa98e2SPeter Wemm **		map -- the database to rebuild.
487c2aa98e2SPeter Wemm **		automatic -- set if this was automatically generated.
488c2aa98e2SPeter Wemm **
489c2aa98e2SPeter Wemm **	Returns:
49040266059SGregory Neil Shapiro **		true if successful; false otherwise.
491c2aa98e2SPeter Wemm **
492c2aa98e2SPeter Wemm **	Side Effects:
493c2aa98e2SPeter Wemm **		Reads the text version of the database, builds the
494c2aa98e2SPeter Wemm **		DBM or DB version.
495c2aa98e2SPeter Wemm */
496c2aa98e2SPeter Wemm 
497c2aa98e2SPeter Wemm bool
498c2aa98e2SPeter Wemm rebuildaliases(map, automatic)
499c2aa98e2SPeter Wemm 	register MAP *map;
500c2aa98e2SPeter Wemm 	bool automatic;
501c2aa98e2SPeter Wemm {
50240266059SGregory Neil Shapiro 	SM_FILE_T *af;
50340266059SGregory Neil Shapiro 	bool nolock = false;
50440266059SGregory Neil Shapiro 	bool success = false;
50506f25ae9SGregory Neil Shapiro 	long sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK;
506c2aa98e2SPeter Wemm 	sigfunc_t oldsigint, oldsigquit;
507c2aa98e2SPeter Wemm #ifdef SIGTSTP
508c2aa98e2SPeter Wemm 	sigfunc_t oldsigtstp;
50906f25ae9SGregory Neil Shapiro #endif /* SIGTSTP */
510c2aa98e2SPeter Wemm 
511c2aa98e2SPeter Wemm 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
51240266059SGregory Neil Shapiro 		return false;
513c2aa98e2SPeter Wemm 
51406f25ae9SGregory Neil Shapiro 	if (!bitnset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail))
515c2aa98e2SPeter Wemm 		sff |= SFF_NOWLINK;
51606f25ae9SGregory Neil Shapiro 	if (!bitnset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail))
517c2aa98e2SPeter Wemm 		sff |= SFF_NOGWFILES;
51806f25ae9SGregory Neil Shapiro 	if (!bitnset(DBS_WORLDWRITABLEALIASFILE, DontBlameSendmail))
519c2aa98e2SPeter Wemm 		sff |= SFF_NOWWFILES;
520c2aa98e2SPeter Wemm 
521c2aa98e2SPeter Wemm 	/* try to lock the source file */
522c2aa98e2SPeter Wemm 	if ((af = safefopen(map->map_file, O_RDWR, 0, sff)) == NULL)
523c2aa98e2SPeter Wemm 	{
524c2aa98e2SPeter Wemm 		struct stat stb;
525c2aa98e2SPeter Wemm 
526c2aa98e2SPeter Wemm 		if ((errno != EACCES && errno != EROFS) || automatic ||
527c2aa98e2SPeter Wemm 		    (af = safefopen(map->map_file, O_RDONLY, 0, sff)) == NULL)
528c2aa98e2SPeter Wemm 		{
529c2aa98e2SPeter Wemm 			int saveerr = errno;
530c2aa98e2SPeter Wemm 
531c2aa98e2SPeter Wemm 			if (tTd(27, 1))
53240266059SGregory Neil Shapiro 				sm_dprintf("Can't open %s: %s\n",
53340266059SGregory Neil Shapiro 					map->map_file, sm_errstring(saveerr));
534c2aa98e2SPeter Wemm 			if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags))
535c2aa98e2SPeter Wemm 				message("newaliases: cannot open %s: %s",
53640266059SGregory Neil Shapiro 					map->map_file, sm_errstring(saveerr));
537c2aa98e2SPeter Wemm 			errno = 0;
53840266059SGregory Neil Shapiro 			return false;
539c2aa98e2SPeter Wemm 		}
54040266059SGregory Neil Shapiro 		nolock = true;
541c2aa98e2SPeter Wemm 		if (tTd(27, 1) ||
54240266059SGregory Neil Shapiro 		    fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &stb) < 0 ||
543c2aa98e2SPeter Wemm 		    bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode))
544c2aa98e2SPeter Wemm 			message("warning: cannot lock %s: %s",
54540266059SGregory Neil Shapiro 				map->map_file, sm_errstring(errno));
546c2aa98e2SPeter Wemm 	}
547c2aa98e2SPeter Wemm 
548c2aa98e2SPeter Wemm 	/* see if someone else is rebuilding the alias file */
549c2aa98e2SPeter Wemm 	if (!nolock &&
55040266059SGregory Neil Shapiro 	    !lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), map->map_file,
55140266059SGregory Neil Shapiro 		      NULL, LOCK_EX|LOCK_NB))
552c2aa98e2SPeter Wemm 	{
553c2aa98e2SPeter Wemm 		/* yes, they are -- wait until done */
554c2aa98e2SPeter Wemm 		message("Alias file %s is locked (maybe being rebuilt)",
555c2aa98e2SPeter Wemm 			map->map_file);
556c2aa98e2SPeter Wemm 		if (OpMode != MD_INITALIAS)
557c2aa98e2SPeter Wemm 		{
558c2aa98e2SPeter Wemm 			/* wait for other rebuild to complete */
55940266059SGregory Neil Shapiro 			(void) lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL),
56040266059SGregory Neil Shapiro 					map->map_file, NULL, LOCK_EX);
561c2aa98e2SPeter Wemm 		}
56240266059SGregory Neil Shapiro 		(void) sm_io_close(af, SM_TIME_DEFAULT);
563c2aa98e2SPeter Wemm 		errno = 0;
56440266059SGregory Neil Shapiro 		return false;
565c2aa98e2SPeter Wemm 	}
566c2aa98e2SPeter Wemm 
56740266059SGregory Neil Shapiro 	oldsigint = sm_signal(SIGINT, SIG_IGN);
56840266059SGregory Neil Shapiro 	oldsigquit = sm_signal(SIGQUIT, SIG_IGN);
569c2aa98e2SPeter Wemm #ifdef SIGTSTP
57040266059SGregory Neil Shapiro 	oldsigtstp = sm_signal(SIGTSTP, SIG_IGN);
57106f25ae9SGregory Neil Shapiro #endif /* SIGTSTP */
572c2aa98e2SPeter Wemm 
573c2aa98e2SPeter Wemm 	if (map->map_class->map_open(map, O_RDWR))
574c2aa98e2SPeter Wemm 	{
575c2aa98e2SPeter Wemm 		if (LogLevel > 7)
576c2aa98e2SPeter Wemm 		{
577c2aa98e2SPeter Wemm 			sm_syslog(LOG_NOTICE, NOQID,
578c2aa98e2SPeter Wemm 				"alias database %s %srebuilt by %s",
579c2aa98e2SPeter Wemm 				map->map_file, automatic ? "auto" : "",
580c2aa98e2SPeter Wemm 				username());
581c2aa98e2SPeter Wemm 		}
582c2aa98e2SPeter Wemm 		map->map_mflags |= MF_OPEN|MF_WRITABLE;
58340266059SGregory Neil Shapiro 		map->map_pid = CurrentPid;
58440266059SGregory Neil Shapiro 		readaliases(map, af, !automatic, true);
58540266059SGregory Neil Shapiro 		success = true;
586c2aa98e2SPeter Wemm 	}
587c2aa98e2SPeter Wemm 	else
588c2aa98e2SPeter Wemm 	{
589c2aa98e2SPeter Wemm 		if (tTd(27, 1))
59040266059SGregory Neil Shapiro 			sm_dprintf("Can't create database for %s: %s\n",
59140266059SGregory Neil Shapiro 				map->map_file, sm_errstring(errno));
592c2aa98e2SPeter Wemm 		if (!automatic)
593c2aa98e2SPeter Wemm 			syserr("Cannot create database for alias file %s",
594c2aa98e2SPeter Wemm 				map->map_file);
595c2aa98e2SPeter Wemm 	}
596c2aa98e2SPeter Wemm 
597c2aa98e2SPeter Wemm 	/* close the file, thus releasing locks */
59840266059SGregory Neil Shapiro 	(void) sm_io_close(af, SM_TIME_DEFAULT);
599c2aa98e2SPeter Wemm 
600c2aa98e2SPeter Wemm 	/* add distinguished entries and close the database */
601c2aa98e2SPeter Wemm 	if (bitset(MF_OPEN, map->map_mflags))
602c2aa98e2SPeter Wemm 	{
6038774250cSGregory Neil Shapiro 		map->map_mflags |= MF_CLOSING;
604c2aa98e2SPeter Wemm 		map->map_class->map_close(map);
6058774250cSGregory Neil Shapiro 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
606c2aa98e2SPeter Wemm 	}
607c2aa98e2SPeter Wemm 
608c2aa98e2SPeter Wemm 	/* restore the old signals */
60940266059SGregory Neil Shapiro 	(void) sm_signal(SIGINT, oldsigint);
61040266059SGregory Neil Shapiro 	(void) sm_signal(SIGQUIT, oldsigquit);
611c2aa98e2SPeter Wemm #ifdef SIGTSTP
61240266059SGregory Neil Shapiro 	(void) sm_signal(SIGTSTP, oldsigtstp);
61306f25ae9SGregory Neil Shapiro #endif /* SIGTSTP */
614c2aa98e2SPeter Wemm 	return success;
615c2aa98e2SPeter Wemm }
61640266059SGregory Neil Shapiro /*
617c2aa98e2SPeter Wemm **  READALIASES -- read and process the alias file.
618c2aa98e2SPeter Wemm **
619c2aa98e2SPeter Wemm **	This routine implements the part of initaliases that occurs
620c2aa98e2SPeter Wemm **	when we are not going to use the DBM stuff.
621c2aa98e2SPeter Wemm **
622c2aa98e2SPeter Wemm **	Parameters:
623c2aa98e2SPeter Wemm **		map -- the alias database descriptor.
624c2aa98e2SPeter Wemm **		af -- file to read the aliases from.
62506f25ae9SGregory Neil Shapiro **		announcestats -- announce statistics regarding number of
626c2aa98e2SPeter Wemm **			aliases, longest alias, etc.
627c2aa98e2SPeter Wemm **		logstats -- lot the same info.
628c2aa98e2SPeter Wemm **
629c2aa98e2SPeter Wemm **	Returns:
630c2aa98e2SPeter Wemm **		none.
631c2aa98e2SPeter Wemm **
632c2aa98e2SPeter Wemm **	Side Effects:
633c2aa98e2SPeter Wemm **		Reads aliasfile into the symbol table.
634c2aa98e2SPeter Wemm **		Optionally, builds the .dir & .pag files.
635c2aa98e2SPeter Wemm */
636c2aa98e2SPeter Wemm 
637c2aa98e2SPeter Wemm void
638c2aa98e2SPeter Wemm readaliases(map, af, announcestats, logstats)
639c2aa98e2SPeter Wemm 	register MAP *map;
64040266059SGregory Neil Shapiro 	SM_FILE_T *af;
641c2aa98e2SPeter Wemm 	bool announcestats;
642c2aa98e2SPeter Wemm 	bool logstats;
643c2aa98e2SPeter Wemm {
644c2aa98e2SPeter Wemm 	register char *p;
645c2aa98e2SPeter Wemm 	char *rhs;
646c2aa98e2SPeter Wemm 	bool skipping;
647c2aa98e2SPeter Wemm 	long naliases, bytes, longest;
648c2aa98e2SPeter Wemm 	ADDRESS al, bl;
649c2aa98e2SPeter Wemm 	char line[BUFSIZ];
650c2aa98e2SPeter Wemm 
651c2aa98e2SPeter Wemm 	/*
652c2aa98e2SPeter Wemm 	**  Read and interpret lines
653c2aa98e2SPeter Wemm 	*/
654c2aa98e2SPeter Wemm 
655c2aa98e2SPeter Wemm 	FileName = map->map_file;
656c2aa98e2SPeter Wemm 	LineNumber = 0;
657c2aa98e2SPeter Wemm 	naliases = bytes = longest = 0;
65840266059SGregory Neil Shapiro 	skipping = false;
65940266059SGregory Neil Shapiro 	while (sm_io_fgets(af, SM_TIME_DEFAULT, line, sizeof line) != NULL)
660c2aa98e2SPeter Wemm 	{
661c2aa98e2SPeter Wemm 		int lhssize, rhssize;
662c2aa98e2SPeter Wemm 		int c;
663c2aa98e2SPeter Wemm 
664c2aa98e2SPeter Wemm 		LineNumber++;
665c2aa98e2SPeter Wemm 		p = strchr(line, '\n');
66640266059SGregory Neil Shapiro 
66740266059SGregory Neil Shapiro 		/* XXX what if line="a\\" ? */
668c2aa98e2SPeter Wemm 		while (p != NULL && p > line && p[-1] == '\\')
669c2aa98e2SPeter Wemm 		{
670c2aa98e2SPeter Wemm 			p--;
67140266059SGregory Neil Shapiro 			if (sm_io_fgets(af, SM_TIME_DEFAULT, p,
67240266059SGregory Neil Shapiro 					SPACELEFT(line, p)) == NULL)
673c2aa98e2SPeter Wemm 				break;
674c2aa98e2SPeter Wemm 			LineNumber++;
675c2aa98e2SPeter Wemm 			p = strchr(p, '\n');
676c2aa98e2SPeter Wemm 		}
677c2aa98e2SPeter Wemm 		if (p != NULL)
678c2aa98e2SPeter Wemm 			*p = '\0';
67940266059SGregory Neil Shapiro 		else if (!sm_io_eof(af))
680c2aa98e2SPeter Wemm 		{
68106f25ae9SGregory Neil Shapiro 			errno = 0;
68206f25ae9SGregory Neil Shapiro 			syserr("554 5.3.0 alias line too long");
683c2aa98e2SPeter Wemm 
684c2aa98e2SPeter Wemm 			/* flush to end of line */
68540266059SGregory Neil Shapiro 			while ((c = sm_io_getc(af, SM_TIME_DEFAULT)) !=
68640266059SGregory Neil Shapiro 				SM_IO_EOF && c != '\n')
687c2aa98e2SPeter Wemm 				continue;
688c2aa98e2SPeter Wemm 
689c2aa98e2SPeter Wemm 			/* skip any continuation lines */
69040266059SGregory Neil Shapiro 			skipping = true;
691c2aa98e2SPeter Wemm 			continue;
692c2aa98e2SPeter Wemm 		}
693c2aa98e2SPeter Wemm 		switch (line[0])
694c2aa98e2SPeter Wemm 		{
695c2aa98e2SPeter Wemm 		  case '#':
696c2aa98e2SPeter Wemm 		  case '\0':
69740266059SGregory Neil Shapiro 			skipping = false;
698c2aa98e2SPeter Wemm 			continue;
699c2aa98e2SPeter Wemm 
700c2aa98e2SPeter Wemm 		  case ' ':
701c2aa98e2SPeter Wemm 		  case '\t':
702c2aa98e2SPeter Wemm 			if (!skipping)
70306f25ae9SGregory Neil Shapiro 				syserr("554 5.3.5 Non-continuation line starts with space");
70440266059SGregory Neil Shapiro 			skipping = true;
705c2aa98e2SPeter Wemm 			continue;
706c2aa98e2SPeter Wemm 		}
70740266059SGregory Neil Shapiro 		skipping = false;
708c2aa98e2SPeter Wemm 
709c2aa98e2SPeter Wemm 		/*
710c2aa98e2SPeter Wemm 		**  Process the LHS
711c2aa98e2SPeter Wemm 		**	Find the colon separator, and parse the address.
712c2aa98e2SPeter Wemm 		**	It should resolve to a local name -- this will
713c2aa98e2SPeter Wemm 		**	be checked later (we want to optionally do
714c2aa98e2SPeter Wemm 		**	parsing of the RHS first to maximize error
715c2aa98e2SPeter Wemm 		**	detection).
716c2aa98e2SPeter Wemm 		*/
717c2aa98e2SPeter Wemm 
718c2aa98e2SPeter Wemm 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
719c2aa98e2SPeter Wemm 			continue;
720c2aa98e2SPeter Wemm 		if (*p++ != ':')
721c2aa98e2SPeter Wemm 		{
72206f25ae9SGregory Neil Shapiro 			syserr("554 5.3.5 missing colon");
723c2aa98e2SPeter Wemm 			continue;
724c2aa98e2SPeter Wemm 		}
72540266059SGregory Neil Shapiro 		if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv, true)
72640266059SGregory Neil Shapiro 		    == NULL)
727c2aa98e2SPeter Wemm 		{
72806f25ae9SGregory Neil Shapiro 			syserr("554 5.3.5 %.40s... illegal alias name", line);
729c2aa98e2SPeter Wemm 			continue;
730c2aa98e2SPeter Wemm 		}
731c2aa98e2SPeter Wemm 
732c2aa98e2SPeter Wemm 		/*
733c2aa98e2SPeter Wemm 		**  Process the RHS.
734c2aa98e2SPeter Wemm 		**	'al' is the internal form of the LHS address.
735c2aa98e2SPeter Wemm 		**	'p' points to the text of the RHS.
736c2aa98e2SPeter Wemm 		*/
737c2aa98e2SPeter Wemm 
738c2aa98e2SPeter Wemm 		while (isascii(*p) && isspace(*p))
739c2aa98e2SPeter Wemm 			p++;
740c2aa98e2SPeter Wemm 		rhs = p;
741c2aa98e2SPeter Wemm 		for (;;)
742c2aa98e2SPeter Wemm 		{
743c2aa98e2SPeter Wemm 			register char *nlp;
744c2aa98e2SPeter Wemm 
745c2aa98e2SPeter Wemm 			nlp = &p[strlen(p)];
746193538b7SGregory Neil Shapiro 			if (nlp > p && nlp[-1] == '\n')
747c2aa98e2SPeter Wemm 				*--nlp = '\0';
748c2aa98e2SPeter Wemm 
749c2aa98e2SPeter Wemm 			if (CheckAliases)
750c2aa98e2SPeter Wemm 			{
751c2aa98e2SPeter Wemm 				/* do parsing & compression of addresses */
752c2aa98e2SPeter Wemm 				while (*p != '\0')
753c2aa98e2SPeter Wemm 				{
754c2aa98e2SPeter Wemm 					auto char *delimptr;
755c2aa98e2SPeter Wemm 
756c2aa98e2SPeter Wemm 					while ((isascii(*p) && isspace(*p)) ||
757c2aa98e2SPeter Wemm 								*p == ',')
758c2aa98e2SPeter Wemm 						p++;
759c2aa98e2SPeter Wemm 					if (*p == '\0')
760c2aa98e2SPeter Wemm 						break;
761c2aa98e2SPeter Wemm 					if (parseaddr(p, &bl, RF_COPYNONE, ',',
76240266059SGregory Neil Shapiro 						      &delimptr, CurEnv, true)
76340266059SGregory Neil Shapiro 					    == NULL)
76406f25ae9SGregory Neil Shapiro 						usrerr("553 5.3.5 %s... bad address", p);
765c2aa98e2SPeter Wemm 					p = delimptr;
766c2aa98e2SPeter Wemm 				}
767c2aa98e2SPeter Wemm 			}
768c2aa98e2SPeter Wemm 			else
769c2aa98e2SPeter Wemm 			{
770c2aa98e2SPeter Wemm 				p = nlp;
771c2aa98e2SPeter Wemm 			}
772c2aa98e2SPeter Wemm 
773c2aa98e2SPeter Wemm 			/* see if there should be a continuation line */
77440266059SGregory Neil Shapiro 			c = sm_io_getc(af, SM_TIME_DEFAULT);
77540266059SGregory Neil Shapiro 			if (!sm_io_eof(af))
77640266059SGregory Neil Shapiro 				(void) sm_io_ungetc(af, SM_TIME_DEFAULT, c);
777c2aa98e2SPeter Wemm 			if (c != ' ' && c != '\t')
778c2aa98e2SPeter Wemm 				break;
779c2aa98e2SPeter Wemm 
780c2aa98e2SPeter Wemm 			/* read continuation line */
78140266059SGregory Neil Shapiro 			if (sm_io_fgets(af, SM_TIME_DEFAULT, p,
78240266059SGregory Neil Shapiro 					sizeof line - (p-line)) == NULL)
783c2aa98e2SPeter Wemm 				break;
784c2aa98e2SPeter Wemm 			LineNumber++;
785c2aa98e2SPeter Wemm 
786c2aa98e2SPeter Wemm 			/* check for line overflow */
78740266059SGregory Neil Shapiro 			if (strchr(p, '\n') == NULL && !sm_io_eof(af))
788c2aa98e2SPeter Wemm 			{
78906f25ae9SGregory Neil Shapiro 				usrerr("554 5.3.5 alias too long");
79040266059SGregory Neil Shapiro 				while ((c = sm_io_getc(af, SM_TIME_DEFAULT))
79140266059SGregory Neil Shapiro 				       != SM_IO_EOF && c != '\n')
792c2aa98e2SPeter Wemm 					continue;
79340266059SGregory Neil Shapiro 				skipping = true;
794c2aa98e2SPeter Wemm 				break;
795c2aa98e2SPeter Wemm 			}
796c2aa98e2SPeter Wemm 		}
797c2aa98e2SPeter Wemm 
798c2aa98e2SPeter Wemm 		if (skipping)
799c2aa98e2SPeter Wemm 			continue;
800c2aa98e2SPeter Wemm 
801c2aa98e2SPeter Wemm 		if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags))
802c2aa98e2SPeter Wemm 		{
80306f25ae9SGregory Neil Shapiro 			syserr("554 5.3.5 %s... cannot alias non-local names",
804c2aa98e2SPeter Wemm 				al.q_paddr);
805c2aa98e2SPeter Wemm 			continue;
806c2aa98e2SPeter Wemm 		}
807c2aa98e2SPeter Wemm 
808c2aa98e2SPeter Wemm 		/*
809c2aa98e2SPeter Wemm 		**  Insert alias into symbol table or database file.
810c2aa98e2SPeter Wemm 		**
811c2aa98e2SPeter Wemm 		**	Special case pOStmaStER -- always make it lower case.
812c2aa98e2SPeter Wemm 		*/
813c2aa98e2SPeter Wemm 
81440266059SGregory Neil Shapiro 		if (sm_strcasecmp(al.q_user, "postmaster") == 0)
815c2aa98e2SPeter Wemm 			makelower(al.q_user);
816c2aa98e2SPeter Wemm 
817c2aa98e2SPeter Wemm 		lhssize = strlen(al.q_user);
818c2aa98e2SPeter Wemm 		rhssize = strlen(rhs);
81906f25ae9SGregory Neil Shapiro 		if (rhssize > 0)
82006f25ae9SGregory Neil Shapiro 		{
82106f25ae9SGregory Neil Shapiro 			/* is RHS empty (just spaces)? */
82206f25ae9SGregory Neil Shapiro 			p = rhs;
82306f25ae9SGregory Neil Shapiro 			while (isascii(*p) && isspace(*p))
82406f25ae9SGregory Neil Shapiro 				p++;
82506f25ae9SGregory Neil Shapiro 		}
82606f25ae9SGregory Neil Shapiro 		if (rhssize == 0 || *p == '\0')
82706f25ae9SGregory Neil Shapiro 		{
82806f25ae9SGregory Neil Shapiro 			syserr("554 5.3.5 %.40s... missing value for alias",
82906f25ae9SGregory Neil Shapiro 			       line);
83006f25ae9SGregory Neil Shapiro 
83106f25ae9SGregory Neil Shapiro 		}
83206f25ae9SGregory Neil Shapiro 		else
83306f25ae9SGregory Neil Shapiro 		{
834c2aa98e2SPeter Wemm 			map->map_class->map_store(map, al.q_user, rhs);
835c2aa98e2SPeter Wemm 
83606f25ae9SGregory Neil Shapiro 			/* statistics */
83706f25ae9SGregory Neil Shapiro 			naliases++;
83806f25ae9SGregory Neil Shapiro 			bytes += lhssize + rhssize;
83906f25ae9SGregory Neil Shapiro 			if (rhssize > longest)
84006f25ae9SGregory Neil Shapiro 				longest = rhssize;
84106f25ae9SGregory Neil Shapiro 		}
84206f25ae9SGregory Neil Shapiro 
84340266059SGregory Neil Shapiro #if 0
84440266059SGregory Neil Shapiro 	/*
84540266059SGregory Neil Shapiro 	**  address strings are now stored in the envelope rpool,
84640266059SGregory Neil Shapiro 	**  and therefore cannot be freed.
84740266059SGregory Neil Shapiro 	*/
848c2aa98e2SPeter Wemm 		if (al.q_paddr != NULL)
84940266059SGregory Neil Shapiro 			sm_free(al.q_paddr);  /* disabled */
850c2aa98e2SPeter Wemm 		if (al.q_host != NULL)
85140266059SGregory Neil Shapiro 			sm_free(al.q_host);  /* disabled */
852c2aa98e2SPeter Wemm 		if (al.q_user != NULL)
85340266059SGregory Neil Shapiro 			sm_free(al.q_user);  /* disabled */
85440266059SGregory Neil Shapiro #endif /* 0 */
855c2aa98e2SPeter Wemm 	}
856c2aa98e2SPeter Wemm 
857c2aa98e2SPeter Wemm 	CurEnv->e_to = NULL;
858c2aa98e2SPeter Wemm 	FileName = NULL;
859c2aa98e2SPeter Wemm 	if (Verbose || announcestats)
86042e5d165SGregory Neil Shapiro 		message("%s: %ld aliases, longest %ld bytes, %ld bytes total",
861c2aa98e2SPeter Wemm 			map->map_file, naliases, longest, bytes);
862c2aa98e2SPeter Wemm 	if (LogLevel > 7 && logstats)
863c2aa98e2SPeter Wemm 		sm_syslog(LOG_INFO, NOQID,
86442e5d165SGregory Neil Shapiro 			"%s: %ld aliases, longest %ld bytes, %ld bytes total",
865c2aa98e2SPeter Wemm 			map->map_file, naliases, longest, bytes);
866c2aa98e2SPeter Wemm }
86740266059SGregory Neil Shapiro /*
868c2aa98e2SPeter Wemm **  FORWARD -- Try to forward mail
869c2aa98e2SPeter Wemm **
870c2aa98e2SPeter Wemm **	This is similar but not identical to aliasing.
871c2aa98e2SPeter Wemm **
872c2aa98e2SPeter Wemm **	Parameters:
873c2aa98e2SPeter Wemm **		user -- the name of the user who's mail we would like
874c2aa98e2SPeter Wemm **			to forward to.  It must have been verified --
875c2aa98e2SPeter Wemm **			i.e., the q_home field must have been filled
876c2aa98e2SPeter Wemm **			in.
877c2aa98e2SPeter Wemm **		sendq -- a pointer to the head of the send queue to
878c2aa98e2SPeter Wemm **			put this user's aliases in.
879c2aa98e2SPeter Wemm **		aliaslevel -- the current alias nesting depth.
880c2aa98e2SPeter Wemm **		e -- the current envelope.
881c2aa98e2SPeter Wemm **
882c2aa98e2SPeter Wemm **	Returns:
883c2aa98e2SPeter Wemm **		none.
884c2aa98e2SPeter Wemm **
885c2aa98e2SPeter Wemm **	Side Effects:
886c2aa98e2SPeter Wemm **		New names are added to send queues.
887c2aa98e2SPeter Wemm */
888c2aa98e2SPeter Wemm 
889c2aa98e2SPeter Wemm void
890c2aa98e2SPeter Wemm forward(user, sendq, aliaslevel, e)
891c2aa98e2SPeter Wemm 	ADDRESS *user;
892c2aa98e2SPeter Wemm 	ADDRESS **sendq;
893c2aa98e2SPeter Wemm 	int aliaslevel;
894c2aa98e2SPeter Wemm 	register ENVELOPE *e;
895c2aa98e2SPeter Wemm {
896c2aa98e2SPeter Wemm 	char *pp;
897c2aa98e2SPeter Wemm 	char *ep;
898c2aa98e2SPeter Wemm 	bool got_transient;
899c2aa98e2SPeter Wemm 
900c2aa98e2SPeter Wemm 	if (tTd(27, 1))
90140266059SGregory Neil Shapiro 		sm_dprintf("forward(%s)\n", user->q_paddr);
902c2aa98e2SPeter Wemm 
903c2aa98e2SPeter Wemm 	if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
90406f25ae9SGregory Neil Shapiro 	    !QS_IS_OK(user->q_state))
905c2aa98e2SPeter Wemm 		return;
90640266059SGregory Neil Shapiro 	if (ForwardPath != NULL && *ForwardPath == '\0')
90740266059SGregory Neil Shapiro 		return;
908c2aa98e2SPeter Wemm 	if (user->q_home == NULL)
909c2aa98e2SPeter Wemm 	{
91006f25ae9SGregory Neil Shapiro 		syserr("554 5.3.0 forward: no home");
911c2aa98e2SPeter Wemm 		user->q_home = "/no/such/directory";
912c2aa98e2SPeter Wemm 	}
913c2aa98e2SPeter Wemm 
914c2aa98e2SPeter Wemm 	/* good address -- look for .forward file in home */
91540266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, 'z', user->q_home);
91640266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, 'u', user->q_user);
91740266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, 'h', user->q_host);
918c2aa98e2SPeter Wemm 	if (ForwardPath == NULL)
919c2aa98e2SPeter Wemm 		ForwardPath = newstr("\201z/.forward");
920c2aa98e2SPeter Wemm 
92140266059SGregory Neil Shapiro 	got_transient = false;
922c2aa98e2SPeter Wemm 	for (pp = ForwardPath; pp != NULL; pp = ep)
923c2aa98e2SPeter Wemm 	{
924c2aa98e2SPeter Wemm 		int err;
92594c01205SGregory Neil Shapiro 		char buf[MAXPATHLEN];
92606f25ae9SGregory Neil Shapiro 		struct stat st;
927c2aa98e2SPeter Wemm 
92806f25ae9SGregory Neil Shapiro 		ep = strchr(pp, SEPARATOR);
929c2aa98e2SPeter Wemm 		if (ep != NULL)
930c2aa98e2SPeter Wemm 			*ep = '\0';
931c2aa98e2SPeter Wemm 		expand(pp, buf, sizeof buf, e);
932c2aa98e2SPeter Wemm 		if (ep != NULL)
93306f25ae9SGregory Neil Shapiro 			*ep++ = SEPARATOR;
934c2aa98e2SPeter Wemm 		if (buf[0] == '\0')
935c2aa98e2SPeter Wemm 			continue;
936c2aa98e2SPeter Wemm 		if (tTd(27, 3))
93740266059SGregory Neil Shapiro 			sm_dprintf("forward: trying %s\n", buf);
938c2aa98e2SPeter Wemm 
93940266059SGregory Neil Shapiro 		err = include(buf, true, user, sendq, aliaslevel, e);
940c2aa98e2SPeter Wemm 		if (err == 0)
941c2aa98e2SPeter Wemm 			break;
942c2aa98e2SPeter Wemm 		else if (transienterror(err))
943c2aa98e2SPeter Wemm 		{
944c2aa98e2SPeter Wemm 			/* we may have to suspend this message */
94540266059SGregory Neil Shapiro 			got_transient = true;
946c2aa98e2SPeter Wemm 			if (tTd(27, 2))
94740266059SGregory Neil Shapiro 				sm_dprintf("forward: transient error on %s\n",
94806f25ae9SGregory Neil Shapiro 					   buf);
949c2aa98e2SPeter Wemm 			if (LogLevel > 2)
95006f25ae9SGregory Neil Shapiro 			{
95106f25ae9SGregory Neil Shapiro 				char *curhost = CurHostName;
95206f25ae9SGregory Neil Shapiro 
95306f25ae9SGregory Neil Shapiro 				CurHostName = NULL;
954c2aa98e2SPeter Wemm 				sm_syslog(LOG_ERR, e->e_id,
955c2aa98e2SPeter Wemm 					  "forward %s: transient error: %s",
95640266059SGregory Neil Shapiro 					  buf, sm_errstring(err));
95706f25ae9SGregory Neil Shapiro 				CurHostName = curhost;
95806f25ae9SGregory Neil Shapiro 			}
95906f25ae9SGregory Neil Shapiro 
960c2aa98e2SPeter Wemm 		}
961c2aa98e2SPeter Wemm 		else
962c2aa98e2SPeter Wemm 		{
963c2aa98e2SPeter Wemm 			switch (err)
964c2aa98e2SPeter Wemm 			{
965c2aa98e2SPeter Wemm 			  case ENOENT:
966c2aa98e2SPeter Wemm 				break;
967c2aa98e2SPeter Wemm 
96806f25ae9SGregory Neil Shapiro 			  case E_SM_WWDIR:
96906f25ae9SGregory Neil Shapiro 			  case E_SM_GWDIR:
97006f25ae9SGregory Neil Shapiro 				/* check if it even exists */
97106f25ae9SGregory Neil Shapiro 				if (stat(buf, &st) < 0 && errno == ENOENT)
97206f25ae9SGregory Neil Shapiro 				{
97306f25ae9SGregory Neil Shapiro 					if (bitnset(DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH,
97406f25ae9SGregory Neil Shapiro 						    DontBlameSendmail))
97506f25ae9SGregory Neil Shapiro 						break;
97606f25ae9SGregory Neil Shapiro 				}
97706f25ae9SGregory Neil Shapiro 				/* FALLTHROUGH */
97806f25ae9SGregory Neil Shapiro 
979c2aa98e2SPeter Wemm #if _FFR_FORWARD_SYSERR
980c2aa98e2SPeter Wemm 			  case E_SM_NOSLINK:
981c2aa98e2SPeter Wemm 			  case E_SM_NOHLINK:
982c2aa98e2SPeter Wemm 			  case E_SM_REGONLY:
983c2aa98e2SPeter Wemm 			  case E_SM_ISEXEC:
984c2aa98e2SPeter Wemm 			  case E_SM_WWFILE:
985c2aa98e2SPeter Wemm 			  case E_SM_GWFILE:
98640266059SGregory Neil Shapiro 				syserr("forward: %s: %s", buf, sm_errstring(err));
987c2aa98e2SPeter Wemm 				break;
98806f25ae9SGregory Neil Shapiro #endif /* _FFR_FORWARD_SYSERR */
989c2aa98e2SPeter Wemm 
990c2aa98e2SPeter Wemm 			  default:
991c2aa98e2SPeter Wemm 				if (LogLevel > (RunAsUid == 0 ? 2 : 10))
992c2aa98e2SPeter Wemm 					sm_syslog(LOG_WARNING, e->e_id,
993c2aa98e2SPeter Wemm 						  "forward %s: %s", buf,
99440266059SGregory Neil Shapiro 						  sm_errstring(err));
995c2aa98e2SPeter Wemm 				if (Verbose)
996c2aa98e2SPeter Wemm 					message("forward: %s: %s",
99740266059SGregory Neil Shapiro 						buf, sm_errstring(err));
998c2aa98e2SPeter Wemm 				break;
999c2aa98e2SPeter Wemm 			}
1000c2aa98e2SPeter Wemm 		}
1001c2aa98e2SPeter Wemm 	}
1002c2aa98e2SPeter Wemm 	if (pp == NULL && got_transient)
1003c2aa98e2SPeter Wemm 	{
1004c2aa98e2SPeter Wemm 		/*
1005c2aa98e2SPeter Wemm 		**  There was no successful .forward open and at least one
1006c2aa98e2SPeter Wemm 		**  transient open.  We have to defer this address for
1007c2aa98e2SPeter Wemm 		**  further delivery.
1008c2aa98e2SPeter Wemm 		*/
1009c2aa98e2SPeter Wemm 
1010c2aa98e2SPeter Wemm 		message("transient .forward open error: message queued");
101106f25ae9SGregory Neil Shapiro 		user->q_state = QS_QUEUEUP;
1012c2aa98e2SPeter Wemm 		return;
1013c2aa98e2SPeter Wemm 	}
1014c2aa98e2SPeter Wemm }
1015