xref: /freebsd/contrib/sendmail/src/alias.c (revision 5b0945b57059d1cde0831d3afea7ec56c7d79508)
1c2aa98e2SPeter Wemm /*
25dd76dd0SGregory Neil Shapiro  * Copyright (c) 1998-2003 Proofpoint, 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 
164313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: alias.c,v 8.221 2013-11-22 20:51:54 ca 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 */
93d0cef73dSGregory 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)
97a7ec597cSGregory Neil Shapiro 				sm_syslog(LOG_WARNING, e->e_id,
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)
110d0cef73dSGregory Neil Shapiro 			e->e_message = sm_rpool_strdup_x(e->e_rpool,
111d0cef73dSGregory Neil Shapiro 						"alias database unavailable");
11240266059SGregory Neil Shapiro 
11340266059SGregory Neil Shapiro 		/* XXX msg only per recipient? */
11440266059SGregory Neil Shapiro 		if (a->q_message == NULL)
11540266059SGregory Neil Shapiro 			a->q_message = "alias database unavailable";
116c2aa98e2SPeter Wemm 		return;
117c2aa98e2SPeter Wemm 	}
118c2aa98e2SPeter Wemm 	if (p == NULL)
119c2aa98e2SPeter Wemm 		return;
120c2aa98e2SPeter Wemm 
121c2aa98e2SPeter Wemm 	/*
122c2aa98e2SPeter Wemm 	**  Match on Alias.
123c2aa98e2SPeter Wemm 	**	Deliver to the target list.
124c2aa98e2SPeter Wemm 	*/
125c2aa98e2SPeter Wemm 
126c2aa98e2SPeter Wemm 	if (tTd(27, 1))
12740266059SGregory Neil Shapiro 		sm_dprintf("%s (%s, %s) aliased to %s\n",
128c2aa98e2SPeter Wemm 			   a->q_paddr, a->q_host, a->q_user, p);
129c2aa98e2SPeter Wemm 	if (bitset(EF_VRFYONLY, e->e_flags))
130c2aa98e2SPeter Wemm 	{
13106f25ae9SGregory Neil Shapiro 		a->q_state = QS_VERIFIED;
132c2aa98e2SPeter Wemm 		return;
133c2aa98e2SPeter Wemm 	}
134c2aa98e2SPeter Wemm 	message("aliased to %s", shortenstring(p, MAXSHORTSTR));
13506f25ae9SGregory Neil Shapiro 	if (LogLevel > 10)
136c2aa98e2SPeter Wemm 		sm_syslog(LOG_INFO, e->e_id,
137c2aa98e2SPeter Wemm 			  "alias %.100s => %s",
138c2aa98e2SPeter Wemm 			  a->q_paddr, shortenstring(p, MAXSHORTSTR));
139c2aa98e2SPeter Wemm 	a->q_flags &= ~QSELFREF;
140c2aa98e2SPeter Wemm 	if (tTd(27, 5))
141c2aa98e2SPeter Wemm 	{
14240266059SGregory Neil Shapiro 		sm_dprintf("alias: QS_EXPANDED ");
143e92d3f3fSGregory Neil Shapiro 		printaddr(sm_debug_file(), a, false);
144c2aa98e2SPeter Wemm 	}
14506f25ae9SGregory Neil Shapiro 	a->q_state = QS_EXPANDED;
14606f25ae9SGregory Neil Shapiro 
14706f25ae9SGregory Neil Shapiro 	/*
14806f25ae9SGregory Neil Shapiro 	**  Always deliver aliased items as the default user.
14906f25ae9SGregory Neil Shapiro 	**  Setting q_gid to 0 forces deliver() to use DefUser
15006f25ae9SGregory Neil Shapiro 	**  instead of the alias name for the call to initgroups().
15106f25ae9SGregory Neil Shapiro 	*/
15206f25ae9SGregory Neil Shapiro 
15306f25ae9SGregory Neil Shapiro 	a->q_uid = DefUid;
15406f25ae9SGregory Neil Shapiro 	a->q_gid = 0;
15506f25ae9SGregory Neil Shapiro 	a->q_fullname = NULL;
15606f25ae9SGregory Neil Shapiro 	a->q_flags |= QGOODUID|QALIAS;
15706f25ae9SGregory Neil Shapiro 
158c2aa98e2SPeter Wemm 	(void) sendtolist(p, a, sendq, aliaslevel + 1, e);
15940266059SGregory Neil Shapiro 
16006f25ae9SGregory Neil Shapiro 	if (bitset(QSELFREF, a->q_flags) && QS_IS_EXPANDED(a->q_state))
16106f25ae9SGregory Neil Shapiro 		a->q_state = QS_OK;
162c2aa98e2SPeter Wemm 
163c2aa98e2SPeter Wemm 	/*
164c2aa98e2SPeter Wemm 	**  Look for owner of alias
165c2aa98e2SPeter Wemm 	*/
166c2aa98e2SPeter Wemm 
167c2aa98e2SPeter Wemm 	if (strncmp(a->q_user, "owner-", 6) == 0 ||
168d0cef73dSGregory Neil Shapiro 	    strlen(a->q_user) > sizeof(obuf) - 7)
169d0cef73dSGregory Neil Shapiro 		(void) sm_strlcpy(obuf, "owner-owner", sizeof(obuf));
170c2aa98e2SPeter Wemm 	else
171d0cef73dSGregory Neil Shapiro 		(void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user);
17240266059SGregory Neil Shapiro 	owner = aliaslookup(obuf, &status, a->q_host);
173c2aa98e2SPeter Wemm 	if (owner == NULL)
174c2aa98e2SPeter Wemm 		return;
175c2aa98e2SPeter Wemm 
176c2aa98e2SPeter Wemm 	/* reflect owner into envelope sender */
177c2aa98e2SPeter Wemm 	if (strpbrk(owner, ",:/|\"") != NULL)
178c2aa98e2SPeter Wemm 		owner = obuf;
17940266059SGregory Neil Shapiro 	a->q_owner = sm_rpool_strdup_x(e->e_rpool, owner);
180c2aa98e2SPeter Wemm 
181c2aa98e2SPeter Wemm 	/* announce delivery to this alias; NORECEIPT bit set later */
182c2aa98e2SPeter Wemm 	if (e->e_xfp != NULL)
18340266059SGregory Neil Shapiro 		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
18440266059SGregory Neil Shapiro 				"Message delivered to mailing list %s\n",
185c2aa98e2SPeter Wemm 				a->q_paddr);
186c2aa98e2SPeter Wemm 	e->e_flags |= EF_SENDRECEIPT;
187c2aa98e2SPeter Wemm 	a->q_flags |= QDELIVERED|QEXPANDED;
188c2aa98e2SPeter Wemm }
18940266059SGregory Neil Shapiro /*
190c2aa98e2SPeter Wemm **  ALIASLOOKUP -- look up a name in the alias file.
191c2aa98e2SPeter Wemm **
192c2aa98e2SPeter Wemm **	Parameters:
193c2aa98e2SPeter Wemm **		name -- the name to look up.
194c2aa98e2SPeter Wemm **		pstat -- a pointer to a place to put the status.
19540266059SGregory Neil Shapiro **		av -- argument for %1 expansion.
196c2aa98e2SPeter Wemm **
197c2aa98e2SPeter Wemm **	Returns:
198c2aa98e2SPeter Wemm **		the value of name.
199c2aa98e2SPeter Wemm **		NULL if unknown.
200c2aa98e2SPeter Wemm **
201c2aa98e2SPeter Wemm **	Side Effects:
202c2aa98e2SPeter Wemm **		none.
203c2aa98e2SPeter Wemm **
204c2aa98e2SPeter Wemm **	Warnings:
205c2aa98e2SPeter Wemm **		The return value will be trashed across calls.
206c2aa98e2SPeter Wemm */
207c2aa98e2SPeter Wemm 
20806f25ae9SGregory Neil Shapiro static char *
20940266059SGregory Neil Shapiro aliaslookup(name, pstat, av)
210c2aa98e2SPeter Wemm 	char *name;
211c2aa98e2SPeter Wemm 	int *pstat;
21240266059SGregory Neil Shapiro 	char *av;
213c2aa98e2SPeter Wemm {
214c2aa98e2SPeter Wemm 	static MAP *map = NULL;
21540266059SGregory Neil Shapiro #if _FFR_ALIAS_DETAIL
21640266059SGregory Neil Shapiro 	int i;
21740266059SGregory Neil Shapiro 	char *argv[4];
218*5b0945b5SGregory Neil Shapiro #endif
219c2aa98e2SPeter Wemm 
220c2aa98e2SPeter Wemm 	if (map == NULL)
221c2aa98e2SPeter Wemm 	{
222c2aa98e2SPeter Wemm 		STAB *s = stab("aliases", ST_MAP, ST_FIND);
223c2aa98e2SPeter Wemm 
224c2aa98e2SPeter Wemm 		if (s == NULL)
225c2aa98e2SPeter Wemm 			return NULL;
226c2aa98e2SPeter Wemm 		map = &s->s_map;
227c2aa98e2SPeter Wemm 	}
22806f25ae9SGregory Neil Shapiro 	DYNOPENMAP(map);
229c2aa98e2SPeter Wemm 
230c2aa98e2SPeter Wemm 	/* special case POstMastER -- always use lower case */
23140266059SGregory Neil Shapiro 	if (sm_strcasecmp(name, "postmaster") == 0)
232c2aa98e2SPeter Wemm 		name = "postmaster";
233c2aa98e2SPeter Wemm 
23440266059SGregory Neil Shapiro #if _FFR_ALIAS_DETAIL
23540266059SGregory Neil Shapiro 	i = 0;
23640266059SGregory Neil Shapiro 	argv[i++] = name;
23740266059SGregory Neil Shapiro 	argv[i++] = av;
23840266059SGregory Neil Shapiro 
23940266059SGregory Neil Shapiro 	/* XXX '+' is hardwired here as delimiter! */
24040266059SGregory Neil Shapiro 	if (av != NULL && *av == '+')
24140266059SGregory Neil Shapiro 		argv[i++] = av + 1;
24240266059SGregory Neil Shapiro 	argv[i++] = NULL;
24340266059SGregory Neil Shapiro 	return (*map->map_class->map_lookup)(map, name, argv, pstat);
24440266059SGregory Neil Shapiro #else /* _FFR_ALIAS_DETAIL */
245c2aa98e2SPeter Wemm 	return (*map->map_class->map_lookup)(map, name, NULL, pstat);
24640266059SGregory Neil Shapiro #endif /* _FFR_ALIAS_DETAIL */
247c2aa98e2SPeter Wemm }
24840266059SGregory Neil Shapiro /*
249c2aa98e2SPeter Wemm **  SETALIAS -- set up an alias map
250c2aa98e2SPeter Wemm **
251c2aa98e2SPeter Wemm **	Called when reading configuration file.
252c2aa98e2SPeter Wemm **
253c2aa98e2SPeter Wemm **	Parameters:
254c2aa98e2SPeter Wemm **		spec -- the alias specification
255c2aa98e2SPeter Wemm **
256c2aa98e2SPeter Wemm **	Returns:
257c2aa98e2SPeter Wemm **		none.
258c2aa98e2SPeter Wemm */
259c2aa98e2SPeter Wemm 
260c2aa98e2SPeter Wemm void
261c2aa98e2SPeter Wemm setalias(spec)
262c2aa98e2SPeter Wemm 	char *spec;
263c2aa98e2SPeter Wemm {
264c2aa98e2SPeter Wemm 	register char *p;
265c2aa98e2SPeter Wemm 	register MAP *map;
266c2aa98e2SPeter Wemm 	char *class;
267c2aa98e2SPeter Wemm 	STAB *s;
268c2aa98e2SPeter Wemm 
269c2aa98e2SPeter Wemm 	if (tTd(27, 8))
27040266059SGregory Neil Shapiro 		sm_dprintf("setalias(%s)\n", spec);
271c2aa98e2SPeter Wemm 
272c2aa98e2SPeter Wemm 	for (p = spec; p != NULL; )
273c2aa98e2SPeter Wemm 	{
274c2aa98e2SPeter Wemm 		char buf[50];
275c2aa98e2SPeter Wemm 
276*5b0945b5SGregory Neil Shapiro 		while (SM_ISSPACE(*p))
277c2aa98e2SPeter Wemm 			p++;
278c2aa98e2SPeter Wemm 		if (*p == '\0')
279c2aa98e2SPeter Wemm 			break;
280c2aa98e2SPeter Wemm 		spec = p;
281c2aa98e2SPeter Wemm 
282c2aa98e2SPeter Wemm 		if (NAliasFileMaps >= MAXMAPSTACK)
283c2aa98e2SPeter Wemm 		{
284c2aa98e2SPeter Wemm 			syserr("Too many alias databases defined, %d max",
285c2aa98e2SPeter Wemm 				MAXMAPSTACK);
286c2aa98e2SPeter Wemm 			return;
287c2aa98e2SPeter Wemm 		}
288c2aa98e2SPeter Wemm 		if (AliasFileMap == NULL)
289c2aa98e2SPeter Wemm 		{
29040266059SGregory Neil Shapiro 			(void) sm_strlcpy(buf, "aliases.files sequence",
291d0cef73dSGregory Neil Shapiro 					  sizeof(buf));
292c2aa98e2SPeter Wemm 			AliasFileMap = makemapentry(buf);
293c2aa98e2SPeter Wemm 			if (AliasFileMap == NULL)
294c2aa98e2SPeter Wemm 			{
295c2aa98e2SPeter Wemm 				syserr("setalias: cannot create aliases.files map");
296c2aa98e2SPeter Wemm 				return;
297c2aa98e2SPeter Wemm 			}
298c2aa98e2SPeter Wemm 		}
299d0cef73dSGregory Neil Shapiro 		(void) sm_snprintf(buf, sizeof(buf), "Alias%d", NAliasFileMaps);
300c2aa98e2SPeter Wemm 		s = stab(buf, ST_MAP, ST_ENTER);
301c2aa98e2SPeter Wemm 		map = &s->s_map;
302d0cef73dSGregory Neil Shapiro 		memset(map, '\0', sizeof(*map));
303c2aa98e2SPeter Wemm 		map->map_mname = s->s_name;
30406f25ae9SGregory Neil Shapiro 		p = strpbrk(p, ALIAS_SPEC_SEPARATORS);
30506f25ae9SGregory Neil Shapiro 		if (p != NULL && *p == SEPARATOR)
306c2aa98e2SPeter Wemm 		{
307c2aa98e2SPeter Wemm 			/* map name */
308c2aa98e2SPeter Wemm 			*p++ = '\0';
309c2aa98e2SPeter Wemm 			class = spec;
310c2aa98e2SPeter Wemm 			spec = p;
311c2aa98e2SPeter Wemm 		}
312c2aa98e2SPeter Wemm 		else
313c2aa98e2SPeter Wemm 		{
314c2aa98e2SPeter Wemm 			class = "implicit";
315c2aa98e2SPeter Wemm 			map->map_mflags = MF_INCLNULL;
316c2aa98e2SPeter Wemm 		}
317c2aa98e2SPeter Wemm 
318c2aa98e2SPeter Wemm 		/* find end of spec */
319c2aa98e2SPeter Wemm 		if (p != NULL)
32006f25ae9SGregory Neil Shapiro 		{
32140266059SGregory Neil Shapiro 			bool quoted = false;
32206f25ae9SGregory Neil Shapiro 
32306f25ae9SGregory Neil Shapiro 			for (; *p != '\0'; p++)
32406f25ae9SGregory Neil Shapiro 			{
32506f25ae9SGregory Neil Shapiro 				/*
32606f25ae9SGregory Neil Shapiro 				**  Don't break into a quoted string.
32706f25ae9SGregory Neil Shapiro 				**  Needed for ldap maps which use
32806f25ae9SGregory Neil Shapiro 				**  commas in their specifications.
32906f25ae9SGregory Neil Shapiro 				*/
33006f25ae9SGregory Neil Shapiro 
33106f25ae9SGregory Neil Shapiro 				if (*p == '"')
33206f25ae9SGregory Neil Shapiro 					quoted = !quoted;
33306f25ae9SGregory Neil Shapiro 				else if (*p == ',' && !quoted)
33406f25ae9SGregory Neil Shapiro 					break;
33506f25ae9SGregory Neil Shapiro 			}
33606f25ae9SGregory Neil Shapiro 
33706f25ae9SGregory Neil Shapiro 			/* No more alias specifications follow */
33806f25ae9SGregory Neil Shapiro 			if (*p == '\0')
33906f25ae9SGregory Neil Shapiro 				p = NULL;
34006f25ae9SGregory Neil Shapiro 		}
341c2aa98e2SPeter Wemm 		if (p != NULL)
342c2aa98e2SPeter Wemm 			*p++ = '\0';
343c2aa98e2SPeter Wemm 
344c2aa98e2SPeter Wemm 		if (tTd(27, 20))
34540266059SGregory Neil Shapiro 			sm_dprintf("  map %s:%s %s\n", class, s->s_name, spec);
346c2aa98e2SPeter Wemm 
347c2aa98e2SPeter Wemm 		/* look up class */
348c2aa98e2SPeter Wemm 		s = stab(class, ST_MAPCLASS, ST_FIND);
349c2aa98e2SPeter Wemm 		if (s == NULL)
350c2aa98e2SPeter Wemm 		{
351c2aa98e2SPeter Wemm 			syserr("setalias: unknown alias class %s", class);
352c2aa98e2SPeter Wemm 		}
353c2aa98e2SPeter Wemm 		else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
354c2aa98e2SPeter Wemm 		{
355c2aa98e2SPeter Wemm 			syserr("setalias: map class %s can't handle aliases",
356c2aa98e2SPeter Wemm 				class);
357c2aa98e2SPeter Wemm 		}
358c2aa98e2SPeter Wemm 		else
359c2aa98e2SPeter Wemm 		{
360c2aa98e2SPeter Wemm 			map->map_class = &s->s_mapclass;
36140266059SGregory Neil Shapiro 			map->map_mflags |= MF_ALIAS;
362c2aa98e2SPeter Wemm 			if (map->map_class->map_parse(map, spec))
363c2aa98e2SPeter Wemm 			{
36440266059SGregory Neil Shapiro 				map->map_mflags |= MF_VALID;
365c2aa98e2SPeter Wemm 				AliasFileMap->map_stack[NAliasFileMaps++] = map;
366c2aa98e2SPeter Wemm 			}
367c2aa98e2SPeter Wemm 		}
368c2aa98e2SPeter Wemm 	}
369c2aa98e2SPeter Wemm }
37040266059SGregory Neil Shapiro /*
371c2aa98e2SPeter Wemm **  ALIASWAIT -- wait for distinguished @:@ token to appear.
372c2aa98e2SPeter Wemm **
373c2aa98e2SPeter Wemm **	This can decide to reopen or rebuild the alias file
374c2aa98e2SPeter Wemm **
375c2aa98e2SPeter Wemm **	Parameters:
376c2aa98e2SPeter Wemm **		map -- a pointer to the map descriptor for this alias file.
377c2aa98e2SPeter Wemm **		ext -- the filename extension (e.g., ".db") for the
378c2aa98e2SPeter Wemm **			database file.
379c2aa98e2SPeter Wemm **		isopen -- if set, the database is already open, and we
380c2aa98e2SPeter Wemm **			should check for validity; otherwise, we are
381c2aa98e2SPeter Wemm **			just checking to see if it should be created.
382c2aa98e2SPeter Wemm **
383c2aa98e2SPeter Wemm **	Returns:
38440266059SGregory Neil Shapiro **		true -- if the database is open when we return.
38540266059SGregory Neil Shapiro **		false -- if the database is closed when we return.
386c2aa98e2SPeter Wemm */
387c2aa98e2SPeter Wemm 
388c2aa98e2SPeter Wemm bool
389c2aa98e2SPeter Wemm aliaswait(map, ext, isopen)
390c2aa98e2SPeter Wemm 	MAP *map;
391c2aa98e2SPeter Wemm 	char *ext;
39206f25ae9SGregory Neil Shapiro 	bool isopen;
393c2aa98e2SPeter Wemm {
39440266059SGregory Neil Shapiro 	bool attimeout = false;
395c2aa98e2SPeter Wemm 	time_t mtime;
396c2aa98e2SPeter Wemm 	struct stat stb;
39794c01205SGregory Neil Shapiro 	char buf[MAXPATHLEN];
398c2aa98e2SPeter Wemm 
399c2aa98e2SPeter Wemm 	if (tTd(27, 3))
40040266059SGregory Neil Shapiro 		sm_dprintf("aliaswait(%s:%s)\n",
401c2aa98e2SPeter Wemm 			   map->map_class->map_cname, map->map_file);
402c2aa98e2SPeter Wemm 	if (bitset(MF_ALIASWAIT, map->map_mflags))
403c2aa98e2SPeter Wemm 		return isopen;
404c2aa98e2SPeter Wemm 	map->map_mflags |= MF_ALIASWAIT;
405c2aa98e2SPeter Wemm 
406c2aa98e2SPeter Wemm 	if (SafeAlias > 0)
407c2aa98e2SPeter Wemm 	{
408c2aa98e2SPeter Wemm 		auto int st;
409c2aa98e2SPeter Wemm 		unsigned int sleeptime = 2;
41040266059SGregory Neil Shapiro 		unsigned int loopcount = 0;	/* only used for debugging */
41140266059SGregory Neil Shapiro 		time_t toolong = curtime() + SafeAlias;
412c2aa98e2SPeter Wemm 
413c2aa98e2SPeter Wemm 		while (isopen &&
414c2aa98e2SPeter Wemm 		       map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
415c2aa98e2SPeter Wemm 		{
416c2aa98e2SPeter Wemm 			if (curtime() > toolong)
417c2aa98e2SPeter Wemm 			{
418c2aa98e2SPeter Wemm 				/* we timed out */
41940266059SGregory Neil Shapiro 				attimeout = true;
420c2aa98e2SPeter Wemm 				break;
421c2aa98e2SPeter Wemm 			}
422c2aa98e2SPeter Wemm 
423c2aa98e2SPeter Wemm 			/*
424c2aa98e2SPeter Wemm 			**  Close and re-open the alias database in case
425c2aa98e2SPeter Wemm 			**  the one is mv'ed instead of cp'ed in.
426c2aa98e2SPeter Wemm 			*/
427c2aa98e2SPeter Wemm 
428c2aa98e2SPeter Wemm 			if (tTd(27, 2))
42940266059SGregory Neil Shapiro 			{
43040266059SGregory Neil Shapiro 				loopcount++;
43140266059SGregory Neil Shapiro 				sm_dprintf("aliaswait: sleeping for %u seconds (loopcount = %u)\n",
43240266059SGregory Neil Shapiro 					   sleeptime, loopcount);
43340266059SGregory Neil Shapiro 			}
434c2aa98e2SPeter Wemm 
4358774250cSGregory Neil Shapiro 			map->map_mflags |= MF_CLOSING;
436c2aa98e2SPeter Wemm 			map->map_class->map_close(map);
4378774250cSGregory Neil Shapiro 			map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
43806f25ae9SGregory Neil Shapiro 			(void) sleep(sleeptime);
439c2aa98e2SPeter Wemm 			sleeptime *= 2;
440c2aa98e2SPeter Wemm 			if (sleeptime > 60)
441c2aa98e2SPeter Wemm 				sleeptime = 60;
442c2aa98e2SPeter Wemm 			isopen = map->map_class->map_open(map, O_RDONLY);
443c2aa98e2SPeter Wemm 		}
444c2aa98e2SPeter Wemm 	}
445c2aa98e2SPeter Wemm 
446c2aa98e2SPeter Wemm 	/* see if we need to go into auto-rebuild mode */
447c2aa98e2SPeter Wemm 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
448c2aa98e2SPeter Wemm 	{
449c2aa98e2SPeter Wemm 		if (tTd(27, 3))
45040266059SGregory Neil Shapiro 			sm_dprintf("aliaswait: not rebuildable\n");
451c2aa98e2SPeter Wemm 		map->map_mflags &= ~MF_ALIASWAIT;
452c2aa98e2SPeter Wemm 		return isopen;
453c2aa98e2SPeter Wemm 	}
454c2aa98e2SPeter Wemm 	if (stat(map->map_file, &stb) < 0)
455c2aa98e2SPeter Wemm 	{
456c2aa98e2SPeter Wemm 		if (tTd(27, 3))
45740266059SGregory Neil Shapiro 			sm_dprintf("aliaswait: no source file\n");
458c2aa98e2SPeter Wemm 		map->map_mflags &= ~MF_ALIASWAIT;
459c2aa98e2SPeter Wemm 		return isopen;
460c2aa98e2SPeter Wemm 	}
461c2aa98e2SPeter Wemm 	mtime = stb.st_mtime;
462d0cef73dSGregory Neil Shapiro 	if (sm_strlcpyn(buf, sizeof(buf), 2,
463d0cef73dSGregory Neil Shapiro 			map->map_file, ext == NULL ? "" : ext) >= sizeof(buf))
46494c01205SGregory Neil Shapiro 	{
46594c01205SGregory Neil Shapiro 		if (LogLevel > 3)
46694c01205SGregory Neil Shapiro 			sm_syslog(LOG_INFO, NOQID,
46794c01205SGregory Neil Shapiro 				  "alias database %s%s name too long",
468c2aa98e2SPeter Wemm 				  map->map_file, ext == NULL ? "" : ext);
46994c01205SGregory Neil Shapiro 		message("alias database %s%s name too long",
47094c01205SGregory Neil Shapiro 			map->map_file, ext == NULL ? "" : ext);
47194c01205SGregory Neil Shapiro 	}
47294c01205SGregory Neil Shapiro 
473c2aa98e2SPeter Wemm 	if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout)
474c2aa98e2SPeter Wemm 	{
475c2aa98e2SPeter Wemm 		if (LogLevel > 3)
476c2aa98e2SPeter Wemm 			sm_syslog(LOG_INFO, NOQID,
47740266059SGregory Neil Shapiro 				  "alias database %s out of date", buf);
478c2aa98e2SPeter Wemm 		message("Warning: alias database %s out of date", buf);
479c2aa98e2SPeter Wemm 	}
480c2aa98e2SPeter Wemm 	map->map_mflags &= ~MF_ALIASWAIT;
481c2aa98e2SPeter Wemm 	return isopen;
482c2aa98e2SPeter Wemm }
48340266059SGregory Neil Shapiro /*
484c2aa98e2SPeter Wemm **  REBUILDALIASES -- rebuild the alias database.
485c2aa98e2SPeter Wemm **
486c2aa98e2SPeter Wemm **	Parameters:
487c2aa98e2SPeter Wemm **		map -- the database to rebuild.
488c2aa98e2SPeter Wemm **		automatic -- set if this was automatically generated.
489c2aa98e2SPeter Wemm **
490c2aa98e2SPeter Wemm **	Returns:
49140266059SGregory Neil Shapiro **		true if successful; false otherwise.
492c2aa98e2SPeter Wemm **
493c2aa98e2SPeter Wemm **	Side Effects:
494c2aa98e2SPeter Wemm **		Reads the text version of the database, builds the
495c2aa98e2SPeter Wemm **		DBM or DB version.
496c2aa98e2SPeter Wemm */
497c2aa98e2SPeter Wemm 
498c2aa98e2SPeter Wemm bool
499c2aa98e2SPeter Wemm rebuildaliases(map, automatic)
500c2aa98e2SPeter Wemm 	register MAP *map;
501c2aa98e2SPeter Wemm 	bool automatic;
502c2aa98e2SPeter Wemm {
50340266059SGregory Neil Shapiro 	SM_FILE_T *af;
50440266059SGregory Neil Shapiro 	bool nolock = false;
50540266059SGregory Neil Shapiro 	bool success = false;
50606f25ae9SGregory Neil Shapiro 	long sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK;
507c2aa98e2SPeter Wemm 	sigfunc_t oldsigint, oldsigquit;
508c2aa98e2SPeter Wemm #ifdef SIGTSTP
509c2aa98e2SPeter Wemm 	sigfunc_t oldsigtstp;
510*5b0945b5SGregory Neil Shapiro #endif
511c2aa98e2SPeter Wemm 
512c2aa98e2SPeter Wemm 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
51340266059SGregory Neil Shapiro 		return false;
514c2aa98e2SPeter Wemm 
51506f25ae9SGregory Neil Shapiro 	if (!bitnset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail))
516c2aa98e2SPeter Wemm 		sff |= SFF_NOWLINK;
51706f25ae9SGregory Neil Shapiro 	if (!bitnset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail))
518c2aa98e2SPeter Wemm 		sff |= SFF_NOGWFILES;
51906f25ae9SGregory Neil Shapiro 	if (!bitnset(DBS_WORLDWRITABLEALIASFILE, DontBlameSendmail))
520c2aa98e2SPeter Wemm 		sff |= SFF_NOWWFILES;
521c2aa98e2SPeter Wemm 
522c2aa98e2SPeter Wemm 	/* try to lock the source file */
523c2aa98e2SPeter Wemm 	if ((af = safefopen(map->map_file, O_RDWR, 0, sff)) == NULL)
524c2aa98e2SPeter Wemm 	{
525c2aa98e2SPeter Wemm 		struct stat stb;
526c2aa98e2SPeter Wemm 
527c2aa98e2SPeter Wemm 		if ((errno != EACCES && errno != EROFS) || automatic ||
528c2aa98e2SPeter Wemm 		    (af = safefopen(map->map_file, O_RDONLY, 0, sff)) == NULL)
529c2aa98e2SPeter Wemm 		{
530c2aa98e2SPeter Wemm 			int saveerr = errno;
531c2aa98e2SPeter Wemm 
532c2aa98e2SPeter Wemm 			if (tTd(27, 1))
53340266059SGregory Neil Shapiro 				sm_dprintf("Can't open %s: %s\n",
53440266059SGregory Neil Shapiro 					map->map_file, sm_errstring(saveerr));
535c2aa98e2SPeter Wemm 			if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags))
536c2aa98e2SPeter Wemm 				message("newaliases: cannot open %s: %s",
53740266059SGregory Neil Shapiro 					map->map_file, sm_errstring(saveerr));
538c2aa98e2SPeter Wemm 			errno = 0;
53940266059SGregory Neil Shapiro 			return false;
540c2aa98e2SPeter Wemm 		}
54140266059SGregory Neil Shapiro 		nolock = true;
542c2aa98e2SPeter Wemm 		if (tTd(27, 1) ||
54340266059SGregory Neil Shapiro 		    fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &stb) < 0 ||
544c2aa98e2SPeter Wemm 		    bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode))
545c2aa98e2SPeter Wemm 			message("warning: cannot lock %s: %s",
54640266059SGregory Neil Shapiro 				map->map_file, sm_errstring(errno));
547c2aa98e2SPeter Wemm 	}
548c2aa98e2SPeter Wemm 
549c2aa98e2SPeter Wemm 	/* see if someone else is rebuilding the alias file */
550c2aa98e2SPeter Wemm 	if (!nolock &&
55140266059SGregory Neil Shapiro 	    !lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), map->map_file,
55240266059SGregory Neil Shapiro 		      NULL, LOCK_EX|LOCK_NB))
553c2aa98e2SPeter Wemm 	{
554c2aa98e2SPeter Wemm 		/* yes, they are -- wait until done */
555c2aa98e2SPeter Wemm 		message("Alias file %s is locked (maybe being rebuilt)",
556c2aa98e2SPeter Wemm 			map->map_file);
557c2aa98e2SPeter Wemm 		if (OpMode != MD_INITALIAS)
558c2aa98e2SPeter Wemm 		{
559c2aa98e2SPeter Wemm 			/* wait for other rebuild to complete */
56040266059SGregory Neil Shapiro 			(void) lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL),
56140266059SGregory Neil Shapiro 					map->map_file, NULL, LOCK_EX);
562c2aa98e2SPeter Wemm 		}
56340266059SGregory Neil Shapiro 		(void) sm_io_close(af, SM_TIME_DEFAULT);
564c2aa98e2SPeter Wemm 		errno = 0;
56540266059SGregory Neil Shapiro 		return false;
566c2aa98e2SPeter Wemm 	}
567c2aa98e2SPeter Wemm 
56840266059SGregory Neil Shapiro 	oldsigint = sm_signal(SIGINT, SIG_IGN);
56940266059SGregory Neil Shapiro 	oldsigquit = sm_signal(SIGQUIT, SIG_IGN);
570c2aa98e2SPeter Wemm #ifdef SIGTSTP
57140266059SGregory Neil Shapiro 	oldsigtstp = sm_signal(SIGTSTP, SIG_IGN);
572*5b0945b5SGregory Neil Shapiro #endif
573c2aa98e2SPeter Wemm 
574c2aa98e2SPeter Wemm 	if (map->map_class->map_open(map, O_RDWR))
575c2aa98e2SPeter Wemm 	{
576c2aa98e2SPeter Wemm 		if (LogLevel > 7)
577c2aa98e2SPeter Wemm 		{
578c2aa98e2SPeter Wemm 			sm_syslog(LOG_NOTICE, NOQID,
579c2aa98e2SPeter Wemm 				"alias database %s %srebuilt by %s",
580c2aa98e2SPeter Wemm 				map->map_file, automatic ? "auto" : "",
581c2aa98e2SPeter Wemm 				username());
582c2aa98e2SPeter Wemm 		}
583c2aa98e2SPeter Wemm 		map->map_mflags |= MF_OPEN|MF_WRITABLE;
58440266059SGregory Neil Shapiro 		map->map_pid = CurrentPid;
58540266059SGregory Neil Shapiro 		readaliases(map, af, !automatic, true);
58640266059SGregory Neil Shapiro 		success = true;
587c2aa98e2SPeter Wemm 	}
588c2aa98e2SPeter Wemm 	else
589c2aa98e2SPeter Wemm 	{
590c2aa98e2SPeter Wemm 		if (tTd(27, 1))
59140266059SGregory Neil Shapiro 			sm_dprintf("Can't create database for %s: %s\n",
59240266059SGregory Neil Shapiro 				map->map_file, sm_errstring(errno));
593c2aa98e2SPeter Wemm 		if (!automatic)
594c2aa98e2SPeter Wemm 			syserr("Cannot create database for alias file %s",
595c2aa98e2SPeter Wemm 				map->map_file);
596c2aa98e2SPeter Wemm 	}
597c2aa98e2SPeter Wemm 
598c2aa98e2SPeter Wemm 	/* close the file, thus releasing locks */
59940266059SGregory Neil Shapiro 	(void) sm_io_close(af, SM_TIME_DEFAULT);
600c2aa98e2SPeter Wemm 
601c2aa98e2SPeter Wemm 	/* add distinguished entries and close the database */
602c2aa98e2SPeter Wemm 	if (bitset(MF_OPEN, map->map_mflags))
603c2aa98e2SPeter Wemm 	{
6048774250cSGregory Neil Shapiro 		map->map_mflags |= MF_CLOSING;
605c2aa98e2SPeter Wemm 		map->map_class->map_close(map);
6068774250cSGregory Neil Shapiro 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
607c2aa98e2SPeter Wemm 	}
608c2aa98e2SPeter Wemm 
609c2aa98e2SPeter Wemm 	/* restore the old signals */
61040266059SGregory Neil Shapiro 	(void) sm_signal(SIGINT, oldsigint);
61140266059SGregory Neil Shapiro 	(void) sm_signal(SIGQUIT, oldsigquit);
612c2aa98e2SPeter Wemm #ifdef SIGTSTP
61340266059SGregory Neil Shapiro 	(void) sm_signal(SIGTSTP, oldsigtstp);
614*5b0945b5SGregory Neil Shapiro #endif
615c2aa98e2SPeter Wemm 	return success;
616c2aa98e2SPeter Wemm }
61740266059SGregory Neil Shapiro /*
618c2aa98e2SPeter Wemm **  READALIASES -- read and process the alias file.
619c2aa98e2SPeter Wemm **
620c2aa98e2SPeter Wemm **	This routine implements the part of initaliases that occurs
621c2aa98e2SPeter Wemm **	when we are not going to use the DBM stuff.
622c2aa98e2SPeter Wemm **
623c2aa98e2SPeter Wemm **	Parameters:
624c2aa98e2SPeter Wemm **		map -- the alias database descriptor.
625c2aa98e2SPeter Wemm **		af -- file to read the aliases from.
62606f25ae9SGregory Neil Shapiro **		announcestats -- announce statistics regarding number of
627c2aa98e2SPeter Wemm **			aliases, longest alias, etc.
628c2aa98e2SPeter Wemm **		logstats -- lot the same info.
629c2aa98e2SPeter Wemm **
630c2aa98e2SPeter Wemm **	Returns:
631c2aa98e2SPeter Wemm **		none.
632c2aa98e2SPeter Wemm **
633c2aa98e2SPeter Wemm **	Side Effects:
634c2aa98e2SPeter Wemm **		Reads aliasfile into the symbol table.
635c2aa98e2SPeter Wemm **		Optionally, builds the .dir & .pag files.
636c2aa98e2SPeter Wemm */
637c2aa98e2SPeter Wemm 
638c2aa98e2SPeter Wemm void
639c2aa98e2SPeter Wemm readaliases(map, af, announcestats, logstats)
640c2aa98e2SPeter Wemm 	register MAP *map;
64140266059SGregory Neil Shapiro 	SM_FILE_T *af;
642c2aa98e2SPeter Wemm 	bool announcestats;
643c2aa98e2SPeter Wemm 	bool logstats;
644c2aa98e2SPeter Wemm {
645c2aa98e2SPeter Wemm 	register char *p;
646c2aa98e2SPeter Wemm 	char *rhs;
647c2aa98e2SPeter Wemm 	bool skipping;
648c2aa98e2SPeter Wemm 	long naliases, bytes, longest;
649c2aa98e2SPeter Wemm 	ADDRESS al, bl;
650c2aa98e2SPeter Wemm 	char line[BUFSIZ];
651c2aa98e2SPeter Wemm 
652c2aa98e2SPeter Wemm 	/*
653c2aa98e2SPeter Wemm 	**  Read and interpret lines
654c2aa98e2SPeter Wemm 	*/
655c2aa98e2SPeter Wemm 
656c2aa98e2SPeter Wemm 	FileName = map->map_file;
657c2aa98e2SPeter Wemm 	LineNumber = 0;
658c2aa98e2SPeter Wemm 	naliases = bytes = longest = 0;
65940266059SGregory Neil Shapiro 	skipping = false;
660552d4955SGregory Neil Shapiro 	while (sm_io_fgets(af, SM_TIME_DEFAULT, line, sizeof(line)) >= 0)
661c2aa98e2SPeter Wemm 	{
662c2aa98e2SPeter Wemm 		int lhssize, rhssize;
663c2aa98e2SPeter Wemm 		int c;
664c2aa98e2SPeter Wemm 
665c2aa98e2SPeter Wemm 		LineNumber++;
666c2aa98e2SPeter Wemm 		p = strchr(line, '\n');
66740266059SGregory Neil Shapiro 
66840266059SGregory Neil Shapiro 		/* XXX what if line="a\\" ? */
669c2aa98e2SPeter Wemm 		while (p != NULL && p > line && p[-1] == '\\')
670c2aa98e2SPeter Wemm 		{
671c2aa98e2SPeter Wemm 			p--;
67240266059SGregory Neil Shapiro 			if (sm_io_fgets(af, SM_TIME_DEFAULT, p,
673552d4955SGregory Neil Shapiro 					SPACELEFT(line, p)) < 0)
674c2aa98e2SPeter Wemm 				break;
675c2aa98e2SPeter Wemm 			LineNumber++;
676c2aa98e2SPeter Wemm 			p = strchr(p, '\n');
677c2aa98e2SPeter Wemm 		}
678c2aa98e2SPeter Wemm 		if (p != NULL)
679c2aa98e2SPeter Wemm 			*p = '\0';
68040266059SGregory Neil Shapiro 		else if (!sm_io_eof(af))
681c2aa98e2SPeter Wemm 		{
68206f25ae9SGregory Neil Shapiro 			errno = 0;
68306f25ae9SGregory Neil Shapiro 			syserr("554 5.3.0 alias line too long");
684c2aa98e2SPeter Wemm 
685c2aa98e2SPeter Wemm 			/* flush to end of line */
68640266059SGregory Neil Shapiro 			while ((c = sm_io_getc(af, SM_TIME_DEFAULT)) !=
68740266059SGregory Neil Shapiro 				SM_IO_EOF && c != '\n')
688c2aa98e2SPeter Wemm 				continue;
689c2aa98e2SPeter Wemm 
690c2aa98e2SPeter Wemm 			/* skip any continuation lines */
69140266059SGregory Neil Shapiro 			skipping = true;
692c2aa98e2SPeter Wemm 			continue;
693c2aa98e2SPeter Wemm 		}
694c2aa98e2SPeter Wemm 		switch (line[0])
695c2aa98e2SPeter Wemm 		{
696c2aa98e2SPeter Wemm 		  case '#':
697c2aa98e2SPeter Wemm 		  case '\0':
69840266059SGregory Neil Shapiro 			skipping = false;
699c2aa98e2SPeter Wemm 			continue;
700c2aa98e2SPeter Wemm 
701c2aa98e2SPeter Wemm 		  case ' ':
702c2aa98e2SPeter Wemm 		  case '\t':
703c2aa98e2SPeter Wemm 			if (!skipping)
70406f25ae9SGregory Neil Shapiro 				syserr("554 5.3.5 Non-continuation line starts with space");
70540266059SGregory Neil Shapiro 			skipping = true;
706c2aa98e2SPeter Wemm 			continue;
707c2aa98e2SPeter Wemm 		}
70840266059SGregory Neil Shapiro 		skipping = false;
709c2aa98e2SPeter Wemm 
710c2aa98e2SPeter Wemm 		/*
711c2aa98e2SPeter Wemm 		**  Process the LHS
712c2aa98e2SPeter Wemm 		**	Find the colon separator, and parse the address.
713c2aa98e2SPeter Wemm 		**	It should resolve to a local name -- this will
714c2aa98e2SPeter Wemm 		**	be checked later (we want to optionally do
715c2aa98e2SPeter Wemm 		**	parsing of the RHS first to maximize error
716c2aa98e2SPeter Wemm 		**	detection).
717c2aa98e2SPeter Wemm 		*/
718c2aa98e2SPeter Wemm 
719c2aa98e2SPeter Wemm 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
720c2aa98e2SPeter Wemm 			continue;
721c2aa98e2SPeter Wemm 		if (*p++ != ':')
722c2aa98e2SPeter Wemm 		{
72306f25ae9SGregory Neil Shapiro 			syserr("554 5.3.5 missing colon");
724c2aa98e2SPeter Wemm 			continue;
725c2aa98e2SPeter Wemm 		}
72640266059SGregory Neil Shapiro 		if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv, true)
72740266059SGregory Neil Shapiro 		    == NULL)
728c2aa98e2SPeter Wemm 		{
72906f25ae9SGregory Neil Shapiro 			syserr("554 5.3.5 %.40s... illegal alias name", line);
730c2aa98e2SPeter Wemm 			continue;
731c2aa98e2SPeter Wemm 		}
732c2aa98e2SPeter Wemm 
733c2aa98e2SPeter Wemm 		/*
734c2aa98e2SPeter Wemm 		**  Process the RHS.
735c2aa98e2SPeter Wemm 		**	'al' is the internal form of the LHS address.
736c2aa98e2SPeter Wemm 		**	'p' points to the text of the RHS.
737c2aa98e2SPeter Wemm 		*/
738c2aa98e2SPeter Wemm 
739*5b0945b5SGregory Neil Shapiro 		while (SM_ISSPACE(*p))
740c2aa98e2SPeter Wemm 			p++;
741c2aa98e2SPeter Wemm 		rhs = p;
742c2aa98e2SPeter Wemm 		for (;;)
743c2aa98e2SPeter Wemm 		{
744c2aa98e2SPeter Wemm 			register char *nlp;
745c2aa98e2SPeter Wemm 
746c2aa98e2SPeter Wemm 			nlp = &p[strlen(p)];
747193538b7SGregory Neil Shapiro 			if (nlp > p && nlp[-1] == '\n')
748c2aa98e2SPeter Wemm 				*--nlp = '\0';
749c2aa98e2SPeter Wemm 
750c2aa98e2SPeter Wemm 			if (CheckAliases)
751c2aa98e2SPeter Wemm 			{
752c2aa98e2SPeter Wemm 				/* do parsing & compression of addresses */
753c2aa98e2SPeter Wemm 				while (*p != '\0')
754c2aa98e2SPeter Wemm 				{
755c2aa98e2SPeter Wemm 					auto char *delimptr;
756c2aa98e2SPeter Wemm 
757*5b0945b5SGregory Neil Shapiro 					while ((SM_ISSPACE(*p)) || *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,
782552d4955SGregory Neil Shapiro 					sizeof(line) - (p-line)) < 0)
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;
823*5b0945b5SGregory Neil Shapiro 			while (SM_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 		}
842c2aa98e2SPeter Wemm 	}
843c2aa98e2SPeter Wemm 
844c2aa98e2SPeter Wemm 	CurEnv->e_to = NULL;
845c2aa98e2SPeter Wemm 	FileName = NULL;
846c2aa98e2SPeter Wemm 	if (Verbose || announcestats)
84742e5d165SGregory Neil Shapiro 		message("%s: %ld aliases, longest %ld bytes, %ld bytes total",
848c2aa98e2SPeter Wemm 			map->map_file, naliases, longest, bytes);
849c2aa98e2SPeter Wemm 	if (LogLevel > 7 && logstats)
850c2aa98e2SPeter Wemm 		sm_syslog(LOG_INFO, NOQID,
85142e5d165SGregory Neil Shapiro 			"%s: %ld aliases, longest %ld bytes, %ld bytes total",
852c2aa98e2SPeter Wemm 			map->map_file, naliases, longest, bytes);
853c2aa98e2SPeter Wemm }
85440266059SGregory Neil Shapiro /*
855c2aa98e2SPeter Wemm **  FORWARD -- Try to forward mail
856c2aa98e2SPeter Wemm **
857c2aa98e2SPeter Wemm **	This is similar but not identical to aliasing.
858c2aa98e2SPeter Wemm **
859c2aa98e2SPeter Wemm **	Parameters:
860c2aa98e2SPeter Wemm **		user -- the name of the user who's mail we would like
861c2aa98e2SPeter Wemm **			to forward to.  It must have been verified --
862c2aa98e2SPeter Wemm **			i.e., the q_home field must have been filled
863c2aa98e2SPeter Wemm **			in.
864c2aa98e2SPeter Wemm **		sendq -- a pointer to the head of the send queue to
865c2aa98e2SPeter Wemm **			put this user's aliases in.
866c2aa98e2SPeter Wemm **		aliaslevel -- the current alias nesting depth.
867c2aa98e2SPeter Wemm **		e -- the current envelope.
868c2aa98e2SPeter Wemm **
869c2aa98e2SPeter Wemm **	Returns:
870c2aa98e2SPeter Wemm **		none.
871c2aa98e2SPeter Wemm **
872c2aa98e2SPeter Wemm **	Side Effects:
873c2aa98e2SPeter Wemm **		New names are added to send queues.
874c2aa98e2SPeter Wemm */
875c2aa98e2SPeter Wemm 
876c2aa98e2SPeter Wemm void
877c2aa98e2SPeter Wemm forward(user, sendq, aliaslevel, e)
878c2aa98e2SPeter Wemm 	ADDRESS *user;
879c2aa98e2SPeter Wemm 	ADDRESS **sendq;
880c2aa98e2SPeter Wemm 	int aliaslevel;
881c2aa98e2SPeter Wemm 	register ENVELOPE *e;
882c2aa98e2SPeter Wemm {
883c2aa98e2SPeter Wemm 	char *pp;
884c2aa98e2SPeter Wemm 	char *ep;
885c2aa98e2SPeter Wemm 	bool got_transient;
886c2aa98e2SPeter Wemm 
887c2aa98e2SPeter Wemm 	if (tTd(27, 1))
88840266059SGregory Neil Shapiro 		sm_dprintf("forward(%s)\n", user->q_paddr);
889c2aa98e2SPeter Wemm 
890c2aa98e2SPeter Wemm 	if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
89106f25ae9SGregory Neil Shapiro 	    !QS_IS_OK(user->q_state))
892c2aa98e2SPeter Wemm 		return;
89340266059SGregory Neil Shapiro 	if (ForwardPath != NULL && *ForwardPath == '\0')
89440266059SGregory Neil Shapiro 		return;
895c2aa98e2SPeter Wemm 	if (user->q_home == NULL)
896c2aa98e2SPeter Wemm 	{
89706f25ae9SGregory Neil Shapiro 		syserr("554 5.3.0 forward: no home");
898c2aa98e2SPeter Wemm 		user->q_home = "/no/such/directory";
899c2aa98e2SPeter Wemm 	}
900c2aa98e2SPeter Wemm 
901c2aa98e2SPeter Wemm 	/* good address -- look for .forward file in home */
90240266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, 'z', user->q_home);
90340266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, 'u', user->q_user);
90440266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, 'h', user->q_host);
905c2aa98e2SPeter Wemm 	if (ForwardPath == NULL)
906c2aa98e2SPeter Wemm 		ForwardPath = newstr("\201z/.forward");
907c2aa98e2SPeter Wemm 
90840266059SGregory Neil Shapiro 	got_transient = false;
909c2aa98e2SPeter Wemm 	for (pp = ForwardPath; pp != NULL; pp = ep)
910c2aa98e2SPeter Wemm 	{
911c2aa98e2SPeter Wemm 		int err;
91294c01205SGregory Neil Shapiro 		char buf[MAXPATHLEN];
91306f25ae9SGregory Neil Shapiro 		struct stat st;
914c2aa98e2SPeter Wemm 
91506f25ae9SGregory Neil Shapiro 		ep = strchr(pp, SEPARATOR);
916c2aa98e2SPeter Wemm 		if (ep != NULL)
917c2aa98e2SPeter Wemm 			*ep = '\0';
918d0cef73dSGregory Neil Shapiro 		expand(pp, buf, sizeof(buf), e);
919c2aa98e2SPeter Wemm 		if (ep != NULL)
92006f25ae9SGregory Neil Shapiro 			*ep++ = SEPARATOR;
921c2aa98e2SPeter Wemm 		if (buf[0] == '\0')
922c2aa98e2SPeter Wemm 			continue;
923c2aa98e2SPeter Wemm 		if (tTd(27, 3))
92440266059SGregory Neil Shapiro 			sm_dprintf("forward: trying %s\n", buf);
925c2aa98e2SPeter Wemm 
92640266059SGregory Neil Shapiro 		err = include(buf, true, user, sendq, aliaslevel, e);
927c2aa98e2SPeter Wemm 		if (err == 0)
928c2aa98e2SPeter Wemm 			break;
929c2aa98e2SPeter Wemm 		else if (transienterror(err))
930c2aa98e2SPeter Wemm 		{
931c2aa98e2SPeter Wemm 			/* we may have to suspend this message */
93240266059SGregory Neil Shapiro 			got_transient = true;
933c2aa98e2SPeter Wemm 			if (tTd(27, 2))
93440266059SGregory Neil Shapiro 				sm_dprintf("forward: transient error on %s\n",
93506f25ae9SGregory Neil Shapiro 					   buf);
936c2aa98e2SPeter Wemm 			if (LogLevel > 2)
93706f25ae9SGregory Neil Shapiro 			{
93806f25ae9SGregory Neil Shapiro 				char *curhost = CurHostName;
93906f25ae9SGregory Neil Shapiro 
94006f25ae9SGregory Neil Shapiro 				CurHostName = NULL;
941c2aa98e2SPeter Wemm 				sm_syslog(LOG_ERR, e->e_id,
942c2aa98e2SPeter Wemm 					  "forward %s: transient error: %s",
94340266059SGregory Neil Shapiro 					  buf, sm_errstring(err));
94406f25ae9SGregory Neil Shapiro 				CurHostName = curhost;
94506f25ae9SGregory Neil Shapiro 			}
94606f25ae9SGregory Neil Shapiro 
947c2aa98e2SPeter Wemm 		}
948c2aa98e2SPeter Wemm 		else
949c2aa98e2SPeter Wemm 		{
950c2aa98e2SPeter Wemm 			switch (err)
951c2aa98e2SPeter Wemm 			{
952c2aa98e2SPeter Wemm 			  case ENOENT:
953c2aa98e2SPeter Wemm 				break;
954c2aa98e2SPeter Wemm 
95506f25ae9SGregory Neil Shapiro 			  case E_SM_WWDIR:
95606f25ae9SGregory Neil Shapiro 			  case E_SM_GWDIR:
95706f25ae9SGregory Neil Shapiro 				/* check if it even exists */
95806f25ae9SGregory Neil Shapiro 				if (stat(buf, &st) < 0 && errno == ENOENT)
95906f25ae9SGregory Neil Shapiro 				{
96006f25ae9SGregory Neil Shapiro 					if (bitnset(DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH,
96106f25ae9SGregory Neil Shapiro 						    DontBlameSendmail))
96206f25ae9SGregory Neil Shapiro 						break;
96306f25ae9SGregory Neil Shapiro 				}
96406f25ae9SGregory Neil Shapiro 				/* FALLTHROUGH */
96506f25ae9SGregory Neil Shapiro 
966c2aa98e2SPeter Wemm #if _FFR_FORWARD_SYSERR
967c2aa98e2SPeter Wemm 			  case E_SM_NOSLINK:
968c2aa98e2SPeter Wemm 			  case E_SM_NOHLINK:
969c2aa98e2SPeter Wemm 			  case E_SM_REGONLY:
970c2aa98e2SPeter Wemm 			  case E_SM_ISEXEC:
971c2aa98e2SPeter Wemm 			  case E_SM_WWFILE:
972c2aa98e2SPeter Wemm 			  case E_SM_GWFILE:
97340266059SGregory Neil Shapiro 				syserr("forward: %s: %s", buf, sm_errstring(err));
974c2aa98e2SPeter Wemm 				break;
97506f25ae9SGregory Neil Shapiro #endif /* _FFR_FORWARD_SYSERR */
976c2aa98e2SPeter Wemm 
977c2aa98e2SPeter Wemm 			  default:
978c2aa98e2SPeter Wemm 				if (LogLevel > (RunAsUid == 0 ? 2 : 10))
979c2aa98e2SPeter Wemm 					sm_syslog(LOG_WARNING, e->e_id,
980c2aa98e2SPeter Wemm 						  "forward %s: %s", buf,
98140266059SGregory Neil Shapiro 						  sm_errstring(err));
982c2aa98e2SPeter Wemm 				if (Verbose)
983c2aa98e2SPeter Wemm 					message("forward: %s: %s",
98440266059SGregory Neil Shapiro 						buf, sm_errstring(err));
985c2aa98e2SPeter Wemm 				break;
986c2aa98e2SPeter Wemm 			}
987c2aa98e2SPeter Wemm 		}
988c2aa98e2SPeter Wemm 	}
989c2aa98e2SPeter Wemm 	if (pp == NULL && got_transient)
990c2aa98e2SPeter Wemm 	{
991c2aa98e2SPeter Wemm 		/*
992c2aa98e2SPeter Wemm 		**  There was no successful .forward open and at least one
993c2aa98e2SPeter Wemm 		**  transient open.  We have to defer this address for
994c2aa98e2SPeter Wemm 		**  further delivery.
995c2aa98e2SPeter Wemm 		*/
996c2aa98e2SPeter Wemm 
997c2aa98e2SPeter Wemm 		message("transient .forward open error: message queued");
99806f25ae9SGregory Neil Shapiro 		user->q_state = QS_QUEUEUP;
999c2aa98e2SPeter Wemm 		return;
1000c2aa98e2SPeter Wemm 	}
1001c2aa98e2SPeter Wemm }
1002