xref: /illumos-gate/usr/src/cmd/sendmail/src/alias.c (revision fec46055b92c6c6721fc6698843744a264e6ca70)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
37c478bd9Sstevel@tonic-gate  *	All rights reserved.
47c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
57c478bd9Sstevel@tonic-gate  * Copyright (c) 1988, 1993
67c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
97c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
107c478bd9Sstevel@tonic-gate  * the sendmail distribution.
117c478bd9Sstevel@tonic-gate  *
127c478bd9Sstevel@tonic-gate  */
137c478bd9Sstevel@tonic-gate 
147c478bd9Sstevel@tonic-gate #include <sendmail.h>
157c478bd9Sstevel@tonic-gate 
16058561cbSjbeck SM_RCSID("@(#)$Id: alias.c,v 8.219 2006/10/24 18:04:09 ca Exp $")
177c478bd9Sstevel@tonic-gate 
187c478bd9Sstevel@tonic-gate #define SEPARATOR ':'
197c478bd9Sstevel@tonic-gate # define ALIAS_SPEC_SEPARATORS	" ,/:"
207c478bd9Sstevel@tonic-gate 
217c478bd9Sstevel@tonic-gate static MAP	*AliasFileMap = NULL;	/* the actual aliases.files map */
227c478bd9Sstevel@tonic-gate static int	NAliasFileMaps;	/* the number of entries in AliasFileMap */
237c478bd9Sstevel@tonic-gate 
247c478bd9Sstevel@tonic-gate static char	*aliaslookup __P((char *, int *, char *));
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate **  ALIAS -- Compute aliases.
287c478bd9Sstevel@tonic-gate **
297c478bd9Sstevel@tonic-gate **	Scans the alias file for an alias for the given address.
307c478bd9Sstevel@tonic-gate **	If found, it arranges to deliver to the alias list instead.
317c478bd9Sstevel@tonic-gate **	Uses libdbm database if -DDBM.
327c478bd9Sstevel@tonic-gate **
337c478bd9Sstevel@tonic-gate **	Parameters:
347c478bd9Sstevel@tonic-gate **		a -- address to alias.
357c478bd9Sstevel@tonic-gate **		sendq -- a pointer to the head of the send queue
367c478bd9Sstevel@tonic-gate **			to put the aliases in.
377c478bd9Sstevel@tonic-gate **		aliaslevel -- the current alias nesting depth.
387c478bd9Sstevel@tonic-gate **		e -- the current envelope.
397c478bd9Sstevel@tonic-gate **
407c478bd9Sstevel@tonic-gate **	Returns:
417c478bd9Sstevel@tonic-gate **		none
427c478bd9Sstevel@tonic-gate **
437c478bd9Sstevel@tonic-gate **	Side Effects:
447c478bd9Sstevel@tonic-gate **		Aliases found are expanded.
457c478bd9Sstevel@tonic-gate **
467c478bd9Sstevel@tonic-gate **	Deficiencies:
477c478bd9Sstevel@tonic-gate **		It should complain about names that are aliased to
487c478bd9Sstevel@tonic-gate **			nothing.
497c478bd9Sstevel@tonic-gate */
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate void
alias(a,sendq,aliaslevel,e)527c478bd9Sstevel@tonic-gate alias(a, sendq, aliaslevel, e)
537c478bd9Sstevel@tonic-gate 	register ADDRESS *a;
547c478bd9Sstevel@tonic-gate 	ADDRESS **sendq;
557c478bd9Sstevel@tonic-gate 	int aliaslevel;
567c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
577c478bd9Sstevel@tonic-gate {
587c478bd9Sstevel@tonic-gate 	register char *p;
597c478bd9Sstevel@tonic-gate 	char *owner;
607c478bd9Sstevel@tonic-gate 	auto int status = EX_OK;
617c478bd9Sstevel@tonic-gate 	char obuf[MAXNAME + 7];
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate 	if (tTd(27, 1))
647c478bd9Sstevel@tonic-gate 		sm_dprintf("alias(%s)\n", a->q_user);
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate 	/* don't realias already aliased names */
677c478bd9Sstevel@tonic-gate 	if (!QS_IS_OK(a->q_state))
687c478bd9Sstevel@tonic-gate 		return;
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate 	if (NoAlias)
717c478bd9Sstevel@tonic-gate 		return;
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate 	e->e_to = a->q_paddr;
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate 	/*
767c478bd9Sstevel@tonic-gate 	**  Look up this name.
777c478bd9Sstevel@tonic-gate 	**
787c478bd9Sstevel@tonic-gate 	**	If the map was unavailable, we will queue this message
797c478bd9Sstevel@tonic-gate 	**	until the map becomes available; otherwise, we could
807c478bd9Sstevel@tonic-gate 	**	bounce messages inappropriately.
817c478bd9Sstevel@tonic-gate 	*/
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate #if _FFR_REDIRECTEMPTY
847c478bd9Sstevel@tonic-gate 	/*
857c478bd9Sstevel@tonic-gate 	**  envelope <> can't be sent to mailing lists, only owner-
867c478bd9Sstevel@tonic-gate 	**  send spam of this type to owner- of the list
877c478bd9Sstevel@tonic-gate 	**  ----  to stop spam from going to mailing lists!
887c478bd9Sstevel@tonic-gate 	*/
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	if (e->e_sender != NULL && *e->e_sender == '\0')
917c478bd9Sstevel@tonic-gate 	{
927c478bd9Sstevel@tonic-gate 		/* Look for owner of alias */
93058561cbSjbeck 		(void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user);
947c478bd9Sstevel@tonic-gate 		if (aliaslookup(obuf, &status, a->q_host) != NULL)
957c478bd9Sstevel@tonic-gate 		{
967c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
977c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_WARNING, e->e_id,
987c478bd9Sstevel@tonic-gate 				       "possible spam from <> to list: %s, redirected to %s\n",
997c478bd9Sstevel@tonic-gate 				       a->q_user, obuf);
1007c478bd9Sstevel@tonic-gate 			a->q_user = sm_rpool_strdup_x(e->e_rpool, obuf);
1017c478bd9Sstevel@tonic-gate 		}
1027c478bd9Sstevel@tonic-gate 	}
1037c478bd9Sstevel@tonic-gate #endif /* _FFR_REDIRECTEMPTY */
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate 	p = aliaslookup(a->q_user, &status, a->q_host);
1067c478bd9Sstevel@tonic-gate 	if (status == EX_TEMPFAIL || status == EX_UNAVAILABLE)
1077c478bd9Sstevel@tonic-gate 	{
1087c478bd9Sstevel@tonic-gate 		a->q_state = QS_QUEUEUP;
1097c478bd9Sstevel@tonic-gate 		if (e->e_message == NULL)
110058561cbSjbeck 			e->e_message = sm_rpool_strdup_x(e->e_rpool,
111058561cbSjbeck 						"alias database unavailable");
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate 		/* XXX msg only per recipient? */
1147c478bd9Sstevel@tonic-gate 		if (a->q_message == NULL)
1157c478bd9Sstevel@tonic-gate 			a->q_message = "alias database unavailable";
1167c478bd9Sstevel@tonic-gate 		return;
1177c478bd9Sstevel@tonic-gate 	}
1187c478bd9Sstevel@tonic-gate 	if (p == NULL)
1197c478bd9Sstevel@tonic-gate 		return;
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate 	/*
1227c478bd9Sstevel@tonic-gate 	**  Match on Alias.
1237c478bd9Sstevel@tonic-gate 	**	Deliver to the target list.
1247c478bd9Sstevel@tonic-gate 	*/
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 	if (tTd(27, 1))
1277c478bd9Sstevel@tonic-gate 		sm_dprintf("%s (%s, %s) aliased to %s\n",
1287c478bd9Sstevel@tonic-gate 			   a->q_paddr, a->q_host, a->q_user, p);
1297c478bd9Sstevel@tonic-gate 	if (bitset(EF_VRFYONLY, e->e_flags))
1307c478bd9Sstevel@tonic-gate 	{
1317c478bd9Sstevel@tonic-gate 		a->q_state = QS_VERIFIED;
1327c478bd9Sstevel@tonic-gate 		return;
1337c478bd9Sstevel@tonic-gate 	}
1347c478bd9Sstevel@tonic-gate 	message("aliased to %s", shortenstring(p, MAXSHORTSTR));
1357c478bd9Sstevel@tonic-gate 	if (LogLevel > 10)
1367c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, e->e_id,
1377c478bd9Sstevel@tonic-gate 			  "alias %.100s => %s",
1387c478bd9Sstevel@tonic-gate 			  a->q_paddr, shortenstring(p, MAXSHORTSTR));
1397c478bd9Sstevel@tonic-gate 	a->q_flags &= ~QSELFREF;
1407c478bd9Sstevel@tonic-gate 	if (tTd(27, 5))
1417c478bd9Sstevel@tonic-gate 	{
1427c478bd9Sstevel@tonic-gate 		sm_dprintf("alias: QS_EXPANDED ");
1437c478bd9Sstevel@tonic-gate 		printaddr(sm_debug_file(), a, false);
1447c478bd9Sstevel@tonic-gate 	}
1457c478bd9Sstevel@tonic-gate 	a->q_state = QS_EXPANDED;
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate 	/*
1487c478bd9Sstevel@tonic-gate 	**  Always deliver aliased items as the default user.
1497c478bd9Sstevel@tonic-gate 	**  Setting q_gid to 0 forces deliver() to use DefUser
1507c478bd9Sstevel@tonic-gate 	**  instead of the alias name for the call to initgroups().
1517c478bd9Sstevel@tonic-gate 	*/
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	a->q_uid = DefUid;
1547c478bd9Sstevel@tonic-gate 	a->q_gid = 0;
1557c478bd9Sstevel@tonic-gate 	a->q_fullname = NULL;
1567c478bd9Sstevel@tonic-gate 	a->q_flags |= QGOODUID|QALIAS;
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	(void) sendtolist(p, a, sendq, aliaslevel + 1, e);
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate 	if (bitset(QSELFREF, a->q_flags) && QS_IS_EXPANDED(a->q_state))
1617c478bd9Sstevel@tonic-gate 		a->q_state = QS_OK;
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	/*
1647c478bd9Sstevel@tonic-gate 	**  Look for owner of alias
1657c478bd9Sstevel@tonic-gate 	*/
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	if (strncmp(a->q_user, "owner-", 6) == 0 ||
168058561cbSjbeck 	    strlen(a->q_user) > sizeof(obuf) - 7)
169058561cbSjbeck 		(void) sm_strlcpy(obuf, "owner-owner", sizeof(obuf));
1707c478bd9Sstevel@tonic-gate 	else
171058561cbSjbeck 		(void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user);
1727c478bd9Sstevel@tonic-gate 	owner = aliaslookup(obuf, &status, a->q_host);
1737c478bd9Sstevel@tonic-gate 	if (owner == NULL)
1747c478bd9Sstevel@tonic-gate 		return;
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 	/* reflect owner into envelope sender */
1777c478bd9Sstevel@tonic-gate 	if (strpbrk(owner, ",:/|\"") != NULL)
1787c478bd9Sstevel@tonic-gate 		owner = obuf;
1797c478bd9Sstevel@tonic-gate 	a->q_owner = sm_rpool_strdup_x(e->e_rpool, owner);
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 	/* announce delivery to this alias; NORECEIPT bit set later */
1827c478bd9Sstevel@tonic-gate 	if (e->e_xfp != NULL)
1837c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
1847c478bd9Sstevel@tonic-gate 				"Message delivered to mailing list %s\n",
1857c478bd9Sstevel@tonic-gate 				a->q_paddr);
1867c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_SENDRECEIPT;
1877c478bd9Sstevel@tonic-gate 	a->q_flags |= QDELIVERED|QEXPANDED;
1887c478bd9Sstevel@tonic-gate }
1897c478bd9Sstevel@tonic-gate /*
1907c478bd9Sstevel@tonic-gate **  ALIASLOOKUP -- look up a name in the alias file.
1917c478bd9Sstevel@tonic-gate **
1927c478bd9Sstevel@tonic-gate **	Parameters:
1937c478bd9Sstevel@tonic-gate **		name -- the name to look up.
1947c478bd9Sstevel@tonic-gate **		pstat -- a pointer to a place to put the status.
1957c478bd9Sstevel@tonic-gate **		av -- argument for %1 expansion.
1967c478bd9Sstevel@tonic-gate **
1977c478bd9Sstevel@tonic-gate **	Returns:
1987c478bd9Sstevel@tonic-gate **		the value of name.
1997c478bd9Sstevel@tonic-gate **		NULL if unknown.
2007c478bd9Sstevel@tonic-gate **
2017c478bd9Sstevel@tonic-gate **	Side Effects:
2027c478bd9Sstevel@tonic-gate **		none.
2037c478bd9Sstevel@tonic-gate **
2047c478bd9Sstevel@tonic-gate **	Warnings:
2057c478bd9Sstevel@tonic-gate **		The return value will be trashed across calls.
2067c478bd9Sstevel@tonic-gate */
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate static char *
aliaslookup(name,pstat,av)2097c478bd9Sstevel@tonic-gate aliaslookup(name, pstat, av)
2107c478bd9Sstevel@tonic-gate 	char *name;
2117c478bd9Sstevel@tonic-gate 	int *pstat;
2127c478bd9Sstevel@tonic-gate 	char *av;
2137c478bd9Sstevel@tonic-gate {
2147c478bd9Sstevel@tonic-gate 	static MAP *map = NULL;
2157c478bd9Sstevel@tonic-gate #if _FFR_ALIAS_DETAIL
2167c478bd9Sstevel@tonic-gate 	int i;
2177c478bd9Sstevel@tonic-gate 	char *argv[4];
2187c478bd9Sstevel@tonic-gate #endif /* _FFR_ALIAS_DETAIL */
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 	if (map == NULL)
2217c478bd9Sstevel@tonic-gate 	{
2227c478bd9Sstevel@tonic-gate 		STAB *s = stab("aliases", ST_MAP, ST_FIND);
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 		if (s == NULL)
2257c478bd9Sstevel@tonic-gate 			return NULL;
2267c478bd9Sstevel@tonic-gate 		map = &s->s_map;
2277c478bd9Sstevel@tonic-gate 	}
2287c478bd9Sstevel@tonic-gate 	DYNOPENMAP(map);
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	/* special case POstMastER -- always use lower case */
2317c478bd9Sstevel@tonic-gate 	if (sm_strcasecmp(name, "postmaster") == 0)
2327c478bd9Sstevel@tonic-gate 		name = "postmaster";
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate #if _FFR_ALIAS_DETAIL
2357c478bd9Sstevel@tonic-gate 	i = 0;
2367c478bd9Sstevel@tonic-gate 	argv[i++] = name;
2377c478bd9Sstevel@tonic-gate 	argv[i++] = av;
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	/* XXX '+' is hardwired here as delimiter! */
2407c478bd9Sstevel@tonic-gate 	if (av != NULL && *av == '+')
2417c478bd9Sstevel@tonic-gate 		argv[i++] = av + 1;
2427c478bd9Sstevel@tonic-gate 	argv[i++] = NULL;
2437c478bd9Sstevel@tonic-gate 	return (*map->map_class->map_lookup)(map, name, argv, pstat);
2447c478bd9Sstevel@tonic-gate #else /* _FFR_ALIAS_DETAIL */
2457c478bd9Sstevel@tonic-gate 	return (*map->map_class->map_lookup)(map, name, NULL, pstat);
2467c478bd9Sstevel@tonic-gate #endif /* _FFR_ALIAS_DETAIL */
2477c478bd9Sstevel@tonic-gate }
2487c478bd9Sstevel@tonic-gate /*
2497c478bd9Sstevel@tonic-gate **  SETALIAS -- set up an alias map
2507c478bd9Sstevel@tonic-gate **
2517c478bd9Sstevel@tonic-gate **	Called when reading configuration file.
2527c478bd9Sstevel@tonic-gate **
2537c478bd9Sstevel@tonic-gate **	Parameters:
2547c478bd9Sstevel@tonic-gate **		spec -- the alias specification
2557c478bd9Sstevel@tonic-gate **
2567c478bd9Sstevel@tonic-gate **	Returns:
2577c478bd9Sstevel@tonic-gate **		none.
2587c478bd9Sstevel@tonic-gate */
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate void
setalias(spec)2617c478bd9Sstevel@tonic-gate setalias(spec)
2627c478bd9Sstevel@tonic-gate 	char *spec;
2637c478bd9Sstevel@tonic-gate {
2647c478bd9Sstevel@tonic-gate 	register char *p;
2657c478bd9Sstevel@tonic-gate 	register MAP *map;
2667c478bd9Sstevel@tonic-gate 	char *class;
2677c478bd9Sstevel@tonic-gate 	STAB *s;
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	if (tTd(27, 8))
2707c478bd9Sstevel@tonic-gate 		sm_dprintf("setalias(%s)\n", spec);
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	for (p = spec; p != NULL; )
2737c478bd9Sstevel@tonic-gate 	{
2747c478bd9Sstevel@tonic-gate 		char buf[50];
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
2777c478bd9Sstevel@tonic-gate 			p++;
2787c478bd9Sstevel@tonic-gate 		if (*p == '\0')
2797c478bd9Sstevel@tonic-gate 			break;
2807c478bd9Sstevel@tonic-gate 		spec = p;
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 		if (NAliasFileMaps >= MAXMAPSTACK)
2837c478bd9Sstevel@tonic-gate 		{
2847c478bd9Sstevel@tonic-gate 			syserr("Too many alias databases defined, %d max",
2857c478bd9Sstevel@tonic-gate 				MAXMAPSTACK);
2867c478bd9Sstevel@tonic-gate 			return;
2877c478bd9Sstevel@tonic-gate 		}
2887c478bd9Sstevel@tonic-gate 		if (AliasFileMap == NULL)
2897c478bd9Sstevel@tonic-gate 		{
2907c478bd9Sstevel@tonic-gate 			(void) sm_strlcpy(buf, "aliases.files sequence",
291058561cbSjbeck 					  sizeof(buf));
2927c478bd9Sstevel@tonic-gate 			AliasFileMap = makemapentry(buf);
2937c478bd9Sstevel@tonic-gate 			if (AliasFileMap == NULL)
2947c478bd9Sstevel@tonic-gate 			{
2957c478bd9Sstevel@tonic-gate 				syserr("setalias: cannot create aliases.files map");
2967c478bd9Sstevel@tonic-gate 				return;
2977c478bd9Sstevel@tonic-gate 			}
2987c478bd9Sstevel@tonic-gate 		}
299058561cbSjbeck 		(void) sm_snprintf(buf, sizeof(buf), "Alias%d", NAliasFileMaps);
3007c478bd9Sstevel@tonic-gate 		s = stab(buf, ST_MAP, ST_ENTER);
3017c478bd9Sstevel@tonic-gate 		map = &s->s_map;
302058561cbSjbeck 		memset(map, '\0', sizeof(*map));
3037c478bd9Sstevel@tonic-gate 		map->map_mname = s->s_name;
3047c478bd9Sstevel@tonic-gate 		p = strpbrk(p, ALIAS_SPEC_SEPARATORS);
3057c478bd9Sstevel@tonic-gate 		if (p != NULL && *p == SEPARATOR)
3067c478bd9Sstevel@tonic-gate 		{
3077c478bd9Sstevel@tonic-gate 			/* map name */
3087c478bd9Sstevel@tonic-gate 			*p++ = '\0';
3097c478bd9Sstevel@tonic-gate 			class = spec;
3107c478bd9Sstevel@tonic-gate 			spec = p;
3117c478bd9Sstevel@tonic-gate 		}
3127c478bd9Sstevel@tonic-gate 		else
3137c478bd9Sstevel@tonic-gate 		{
3147c478bd9Sstevel@tonic-gate 			class = "implicit";
3157c478bd9Sstevel@tonic-gate 			map->map_mflags = MF_INCLNULL;
3167c478bd9Sstevel@tonic-gate 		}
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 		/* find end of spec */
3197c478bd9Sstevel@tonic-gate 		if (p != NULL)
3207c478bd9Sstevel@tonic-gate 		{
3217c478bd9Sstevel@tonic-gate 			bool quoted = false;
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 			for (; *p != '\0'; p++)
3247c478bd9Sstevel@tonic-gate 			{
3257c478bd9Sstevel@tonic-gate 				/*
3267c478bd9Sstevel@tonic-gate 				**  Don't break into a quoted string.
3277c478bd9Sstevel@tonic-gate 				**  Needed for ldap maps which use
3287c478bd9Sstevel@tonic-gate 				**  commas in their specifications.
3297c478bd9Sstevel@tonic-gate 				*/
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 				if (*p == '"')
3327c478bd9Sstevel@tonic-gate 					quoted = !quoted;
3337c478bd9Sstevel@tonic-gate 				else if (*p == ',' && !quoted)
3347c478bd9Sstevel@tonic-gate 					break;
3357c478bd9Sstevel@tonic-gate 			}
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 			/* No more alias specifications follow */
3387c478bd9Sstevel@tonic-gate 			if (*p == '\0')
3397c478bd9Sstevel@tonic-gate 				p = NULL;
3407c478bd9Sstevel@tonic-gate 		}
3417c478bd9Sstevel@tonic-gate 		if (p != NULL)
3427c478bd9Sstevel@tonic-gate 			*p++ = '\0';
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 		if (tTd(27, 20))
3457c478bd9Sstevel@tonic-gate 			sm_dprintf("  map %s:%s %s\n", class, s->s_name, spec);
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 		/* look up class */
3487c478bd9Sstevel@tonic-gate 		s = stab(class, ST_MAPCLASS, ST_FIND);
3497c478bd9Sstevel@tonic-gate 		if (s == NULL)
3507c478bd9Sstevel@tonic-gate 		{
3517c478bd9Sstevel@tonic-gate 			syserr("setalias: unknown alias class %s", class);
3527c478bd9Sstevel@tonic-gate 		}
3537c478bd9Sstevel@tonic-gate 		else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
3547c478bd9Sstevel@tonic-gate 		{
3557c478bd9Sstevel@tonic-gate 			syserr("setalias: map class %s can't handle aliases",
3567c478bd9Sstevel@tonic-gate 				class);
3577c478bd9Sstevel@tonic-gate 		}
3587c478bd9Sstevel@tonic-gate 		else
3597c478bd9Sstevel@tonic-gate 		{
3607c478bd9Sstevel@tonic-gate 			map->map_class = &s->s_mapclass;
3617c478bd9Sstevel@tonic-gate 			map->map_mflags |= MF_ALIAS;
3627c478bd9Sstevel@tonic-gate 			if (map->map_class->map_parse(map, spec))
3637c478bd9Sstevel@tonic-gate 			{
3647c478bd9Sstevel@tonic-gate 				map->map_mflags |= MF_VALID;
3657c478bd9Sstevel@tonic-gate 				AliasFileMap->map_stack[NAliasFileMaps++] = map;
3667c478bd9Sstevel@tonic-gate 			}
3677c478bd9Sstevel@tonic-gate 		}
3687c478bd9Sstevel@tonic-gate 	}
3697c478bd9Sstevel@tonic-gate }
3707c478bd9Sstevel@tonic-gate /*
3717c478bd9Sstevel@tonic-gate **  ALIASWAIT -- wait for distinguished @:@ token to appear.
3727c478bd9Sstevel@tonic-gate **
3737c478bd9Sstevel@tonic-gate **	This can decide to reopen or rebuild the alias file
3747c478bd9Sstevel@tonic-gate **
3757c478bd9Sstevel@tonic-gate **	Parameters:
3767c478bd9Sstevel@tonic-gate **		map -- a pointer to the map descriptor for this alias file.
3777c478bd9Sstevel@tonic-gate **		ext -- the filename extension (e.g., ".db") for the
3787c478bd9Sstevel@tonic-gate **			database file.
3797c478bd9Sstevel@tonic-gate **		isopen -- if set, the database is already open, and we
3807c478bd9Sstevel@tonic-gate **			should check for validity; otherwise, we are
3817c478bd9Sstevel@tonic-gate **			just checking to see if it should be created.
3827c478bd9Sstevel@tonic-gate **
3837c478bd9Sstevel@tonic-gate **	Returns:
3847c478bd9Sstevel@tonic-gate **		true -- if the database is open when we return.
3857c478bd9Sstevel@tonic-gate **		false -- if the database is closed when we return.
3867c478bd9Sstevel@tonic-gate */
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate bool
aliaswait(map,ext,isopen)3897c478bd9Sstevel@tonic-gate aliaswait(map, ext, isopen)
3907c478bd9Sstevel@tonic-gate 	MAP *map;
3917c478bd9Sstevel@tonic-gate 	char *ext;
3927c478bd9Sstevel@tonic-gate 	bool isopen;
3937c478bd9Sstevel@tonic-gate {
3947c478bd9Sstevel@tonic-gate 	bool attimeout = false;
3957c478bd9Sstevel@tonic-gate 	time_t mtime;
3967c478bd9Sstevel@tonic-gate 	struct stat stb;
3977c478bd9Sstevel@tonic-gate 	char buf[MAXPATHLEN];
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	if (tTd(27, 3))
4007c478bd9Sstevel@tonic-gate 		sm_dprintf("aliaswait(%s:%s)\n",
4017c478bd9Sstevel@tonic-gate 			   map->map_class->map_cname, map->map_file);
4027c478bd9Sstevel@tonic-gate 	if (bitset(MF_ALIASWAIT, map->map_mflags))
4037c478bd9Sstevel@tonic-gate 		return isopen;
4047c478bd9Sstevel@tonic-gate 	map->map_mflags |= MF_ALIASWAIT;
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	if (SafeAlias > 0)
4077c478bd9Sstevel@tonic-gate 	{
4087c478bd9Sstevel@tonic-gate 		auto int st;
4097c478bd9Sstevel@tonic-gate 		unsigned int sleeptime = 2;
4107c478bd9Sstevel@tonic-gate 		unsigned int loopcount = 0;	/* only used for debugging */
4117c478bd9Sstevel@tonic-gate 		time_t toolong = curtime() + SafeAlias;
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 		while (isopen &&
4147c478bd9Sstevel@tonic-gate 		       map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
4157c478bd9Sstevel@tonic-gate 		{
4167c478bd9Sstevel@tonic-gate 			if (curtime() > toolong)
4177c478bd9Sstevel@tonic-gate 			{
4187c478bd9Sstevel@tonic-gate 				/* we timed out */
4197c478bd9Sstevel@tonic-gate 				attimeout = true;
4207c478bd9Sstevel@tonic-gate 				break;
4217c478bd9Sstevel@tonic-gate 			}
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 			/*
4247c478bd9Sstevel@tonic-gate 			**  Close and re-open the alias database in case
4257c478bd9Sstevel@tonic-gate 			**  the one is mv'ed instead of cp'ed in.
4267c478bd9Sstevel@tonic-gate 			*/
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 			if (tTd(27, 2))
4297c478bd9Sstevel@tonic-gate 			{
4307c478bd9Sstevel@tonic-gate 				loopcount++;
4317c478bd9Sstevel@tonic-gate 				sm_dprintf("aliaswait: sleeping for %u seconds (loopcount = %u)\n",
4327c478bd9Sstevel@tonic-gate 					   sleeptime, loopcount);
4337c478bd9Sstevel@tonic-gate 			}
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 			map->map_mflags |= MF_CLOSING;
4367c478bd9Sstevel@tonic-gate 			map->map_class->map_close(map);
4377c478bd9Sstevel@tonic-gate 			map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
4387c478bd9Sstevel@tonic-gate 			(void) sleep(sleeptime);
4397c478bd9Sstevel@tonic-gate 			sleeptime *= 2;
4407c478bd9Sstevel@tonic-gate 			if (sleeptime > 60)
4417c478bd9Sstevel@tonic-gate 				sleeptime = 60;
4427c478bd9Sstevel@tonic-gate 			isopen = map->map_class->map_open(map, O_RDONLY);
4437c478bd9Sstevel@tonic-gate 		}
4447c478bd9Sstevel@tonic-gate 	}
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	/* see if we need to go into auto-rebuild mode */
4477c478bd9Sstevel@tonic-gate 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
4487c478bd9Sstevel@tonic-gate 	{
4497c478bd9Sstevel@tonic-gate 		if (tTd(27, 3))
4507c478bd9Sstevel@tonic-gate 			sm_dprintf("aliaswait: not rebuildable\n");
4517c478bd9Sstevel@tonic-gate 		map->map_mflags &= ~MF_ALIASWAIT;
4527c478bd9Sstevel@tonic-gate 		return isopen;
4537c478bd9Sstevel@tonic-gate 	}
4547c478bd9Sstevel@tonic-gate 	if (stat(map->map_file, &stb) < 0)
4557c478bd9Sstevel@tonic-gate 	{
4567c478bd9Sstevel@tonic-gate 		if (tTd(27, 3))
4577c478bd9Sstevel@tonic-gate 			sm_dprintf("aliaswait: no source file\n");
4587c478bd9Sstevel@tonic-gate 		map->map_mflags &= ~MF_ALIASWAIT;
4597c478bd9Sstevel@tonic-gate 		return isopen;
4607c478bd9Sstevel@tonic-gate 	}
4617c478bd9Sstevel@tonic-gate 	mtime = stb.st_mtime;
462058561cbSjbeck 	if (sm_strlcpyn(buf, sizeof(buf), 2,
463058561cbSjbeck 			map->map_file, ext == NULL ? "" : ext) >= sizeof(buf))
4647c478bd9Sstevel@tonic-gate 	{
4657c478bd9Sstevel@tonic-gate 		if (LogLevel > 3)
4667c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID,
4677c478bd9Sstevel@tonic-gate 				  "alias database %s%s name too long",
4687c478bd9Sstevel@tonic-gate 				  map->map_file, ext == NULL ? "" : ext);
4697c478bd9Sstevel@tonic-gate 		message("alias database %s%s name too long",
4707c478bd9Sstevel@tonic-gate 			map->map_file, ext == NULL ? "" : ext);
4717c478bd9Sstevel@tonic-gate 	}
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout)
4747c478bd9Sstevel@tonic-gate 	{
4757c478bd9Sstevel@tonic-gate 		if (LogLevel > 3)
4767c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID,
4777c478bd9Sstevel@tonic-gate 				  "alias database %s out of date", buf);
4787c478bd9Sstevel@tonic-gate 		message("Warning: alias database %s out of date", buf);
4797c478bd9Sstevel@tonic-gate 	}
4807c478bd9Sstevel@tonic-gate 	map->map_mflags &= ~MF_ALIASWAIT;
4817c478bd9Sstevel@tonic-gate 	return isopen;
4827c478bd9Sstevel@tonic-gate }
4837c478bd9Sstevel@tonic-gate /*
4847c478bd9Sstevel@tonic-gate **  REBUILDALIASES -- rebuild the alias database.
4857c478bd9Sstevel@tonic-gate **
4867c478bd9Sstevel@tonic-gate **	Parameters:
4877c478bd9Sstevel@tonic-gate **		map -- the database to rebuild.
4887c478bd9Sstevel@tonic-gate **		automatic -- set if this was automatically generated.
4897c478bd9Sstevel@tonic-gate **
4907c478bd9Sstevel@tonic-gate **	Returns:
4917c478bd9Sstevel@tonic-gate **		true if successful; false otherwise.
4927c478bd9Sstevel@tonic-gate **
4937c478bd9Sstevel@tonic-gate **	Side Effects:
4947c478bd9Sstevel@tonic-gate **		Reads the text version of the database, builds the
4957c478bd9Sstevel@tonic-gate **		DBM or DB version.
4967c478bd9Sstevel@tonic-gate */
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate bool
rebuildaliases(map,automatic)4997c478bd9Sstevel@tonic-gate rebuildaliases(map, automatic)
5007c478bd9Sstevel@tonic-gate 	register MAP *map;
5017c478bd9Sstevel@tonic-gate 	bool automatic;
5027c478bd9Sstevel@tonic-gate {
5037c478bd9Sstevel@tonic-gate 	SM_FILE_T *af;
5047c478bd9Sstevel@tonic-gate 	bool nolock = false;
5057c478bd9Sstevel@tonic-gate 	bool success = false;
5067c478bd9Sstevel@tonic-gate 	long sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK;
5077c478bd9Sstevel@tonic-gate 	sigfunc_t oldsigint, oldsigquit;
5087c478bd9Sstevel@tonic-gate #ifdef SIGTSTP
5097c478bd9Sstevel@tonic-gate 	sigfunc_t oldsigtstp;
5107c478bd9Sstevel@tonic-gate #endif /* SIGTSTP */
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
5137c478bd9Sstevel@tonic-gate 		return false;
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate 	if (!bitnset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail))
5167c478bd9Sstevel@tonic-gate 		sff |= SFF_NOWLINK;
5177c478bd9Sstevel@tonic-gate 	if (!bitnset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail))
5187c478bd9Sstevel@tonic-gate 		sff |= SFF_NOGWFILES;
5197c478bd9Sstevel@tonic-gate 	if (!bitnset(DBS_WORLDWRITABLEALIASFILE, DontBlameSendmail))
5207c478bd9Sstevel@tonic-gate 		sff |= SFF_NOWWFILES;
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 	/* try to lock the source file */
5237c478bd9Sstevel@tonic-gate 	if ((af = safefopen(map->map_file, O_RDWR, 0, sff)) == NULL)
5247c478bd9Sstevel@tonic-gate 	{
5257c478bd9Sstevel@tonic-gate 		struct stat stb;
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 		if ((errno != EACCES && errno != EROFS) || automatic ||
5287c478bd9Sstevel@tonic-gate 		    (af = safefopen(map->map_file, O_RDONLY, 0, sff)) == NULL)
5297c478bd9Sstevel@tonic-gate 		{
5307c478bd9Sstevel@tonic-gate 			int saveerr = errno;
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 			if (tTd(27, 1))
5337c478bd9Sstevel@tonic-gate 				sm_dprintf("Can't open %s: %s\n",
5347c478bd9Sstevel@tonic-gate 					map->map_file, sm_errstring(saveerr));
5357c478bd9Sstevel@tonic-gate 			if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags))
5367c478bd9Sstevel@tonic-gate 				message("newaliases: cannot open %s: %s",
5377c478bd9Sstevel@tonic-gate 					map->map_file, sm_errstring(saveerr));
5387c478bd9Sstevel@tonic-gate 			errno = 0;
5397c478bd9Sstevel@tonic-gate 			return false;
5407c478bd9Sstevel@tonic-gate 		}
5417c478bd9Sstevel@tonic-gate 		nolock = true;
5427c478bd9Sstevel@tonic-gate 		if (tTd(27, 1) ||
5437c478bd9Sstevel@tonic-gate 		    fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &stb) < 0 ||
5447c478bd9Sstevel@tonic-gate 		    bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode))
5457c478bd9Sstevel@tonic-gate 			message("warning: cannot lock %s: %s",
5467c478bd9Sstevel@tonic-gate 				map->map_file, sm_errstring(errno));
5477c478bd9Sstevel@tonic-gate 	}
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	/* see if someone else is rebuilding the alias file */
5507c478bd9Sstevel@tonic-gate 	if (!nolock &&
5517c478bd9Sstevel@tonic-gate 	    !lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), map->map_file,
5527c478bd9Sstevel@tonic-gate 		      NULL, LOCK_EX|LOCK_NB))
5537c478bd9Sstevel@tonic-gate 	{
5547c478bd9Sstevel@tonic-gate 		/* yes, they are -- wait until done */
5557c478bd9Sstevel@tonic-gate 		message("Alias file %s is locked (maybe being rebuilt)",
5567c478bd9Sstevel@tonic-gate 			map->map_file);
5577c478bd9Sstevel@tonic-gate 		if (OpMode != MD_INITALIAS)
5587c478bd9Sstevel@tonic-gate 		{
5597c478bd9Sstevel@tonic-gate 			/* wait for other rebuild to complete */
5607c478bd9Sstevel@tonic-gate 			(void) lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL),
5617c478bd9Sstevel@tonic-gate 					map->map_file, NULL, LOCK_EX);
5627c478bd9Sstevel@tonic-gate 		}
5637c478bd9Sstevel@tonic-gate 		(void) sm_io_close(af, SM_TIME_DEFAULT);
5647c478bd9Sstevel@tonic-gate 		errno = 0;
5657c478bd9Sstevel@tonic-gate 		return false;
5667c478bd9Sstevel@tonic-gate 	}
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate 	oldsigint = sm_signal(SIGINT, SIG_IGN);
5697c478bd9Sstevel@tonic-gate 	oldsigquit = sm_signal(SIGQUIT, SIG_IGN);
5707c478bd9Sstevel@tonic-gate #ifdef SIGTSTP
5717c478bd9Sstevel@tonic-gate 	oldsigtstp = sm_signal(SIGTSTP, SIG_IGN);
5727c478bd9Sstevel@tonic-gate #endif /* SIGTSTP */
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	if (map->map_class->map_open(map, O_RDWR))
5757c478bd9Sstevel@tonic-gate 	{
5767c478bd9Sstevel@tonic-gate 		if (LogLevel > 7)
5777c478bd9Sstevel@tonic-gate 		{
5787c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_NOTICE, NOQID,
5797c478bd9Sstevel@tonic-gate 				"alias database %s %srebuilt by %s",
5807c478bd9Sstevel@tonic-gate 				map->map_file, automatic ? "auto" : "",
5817c478bd9Sstevel@tonic-gate 				username());
5827c478bd9Sstevel@tonic-gate 		}
5837c478bd9Sstevel@tonic-gate 		map->map_mflags |= MF_OPEN|MF_WRITABLE;
5847c478bd9Sstevel@tonic-gate 		map->map_pid = CurrentPid;
5857c478bd9Sstevel@tonic-gate 		readaliases(map, af, !automatic, true);
5867c478bd9Sstevel@tonic-gate 		success = true;
5877c478bd9Sstevel@tonic-gate 	}
5887c478bd9Sstevel@tonic-gate 	else
5897c478bd9Sstevel@tonic-gate 	{
5907c478bd9Sstevel@tonic-gate 		if (tTd(27, 1))
5917c478bd9Sstevel@tonic-gate 			sm_dprintf("Can't create database for %s: %s\n",
5927c478bd9Sstevel@tonic-gate 				map->map_file, sm_errstring(errno));
5937c478bd9Sstevel@tonic-gate 		if (!automatic)
5947c478bd9Sstevel@tonic-gate 			syserr("Cannot create database for alias file %s",
5957c478bd9Sstevel@tonic-gate 				map->map_file);
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	/* close the file, thus releasing locks */
5997c478bd9Sstevel@tonic-gate 	(void) sm_io_close(af, SM_TIME_DEFAULT);
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	/* add distinguished entries and close the database */
6027c478bd9Sstevel@tonic-gate 	if (bitset(MF_OPEN, map->map_mflags))
6037c478bd9Sstevel@tonic-gate 	{
6047c478bd9Sstevel@tonic-gate 		map->map_mflags |= MF_CLOSING;
6057c478bd9Sstevel@tonic-gate 		map->map_class->map_close(map);
6067c478bd9Sstevel@tonic-gate 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
6077c478bd9Sstevel@tonic-gate 	}
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	/* restore the old signals */
6107c478bd9Sstevel@tonic-gate 	(void) sm_signal(SIGINT, oldsigint);
6117c478bd9Sstevel@tonic-gate 	(void) sm_signal(SIGQUIT, oldsigquit);
6127c478bd9Sstevel@tonic-gate #ifdef SIGTSTP
6137c478bd9Sstevel@tonic-gate 	(void) sm_signal(SIGTSTP, oldsigtstp);
6147c478bd9Sstevel@tonic-gate #endif /* SIGTSTP */
6157c478bd9Sstevel@tonic-gate 	return success;
6167c478bd9Sstevel@tonic-gate }
6177c478bd9Sstevel@tonic-gate /*
6187c478bd9Sstevel@tonic-gate **  READALIASES -- read and process the alias file.
6197c478bd9Sstevel@tonic-gate **
6207c478bd9Sstevel@tonic-gate **	This routine implements the part of initaliases that occurs
6217c478bd9Sstevel@tonic-gate **	when we are not going to use the DBM stuff.
6227c478bd9Sstevel@tonic-gate **
6237c478bd9Sstevel@tonic-gate **	Parameters:
6247c478bd9Sstevel@tonic-gate **		map -- the alias database descriptor.
6257c478bd9Sstevel@tonic-gate **		af -- file to read the aliases from.
6267c478bd9Sstevel@tonic-gate **		announcestats -- announce statistics regarding number of
6277c478bd9Sstevel@tonic-gate **			aliases, longest alias, etc.
6287c478bd9Sstevel@tonic-gate **		logstats -- lot the same info.
6297c478bd9Sstevel@tonic-gate **
6307c478bd9Sstevel@tonic-gate **	Returns:
6317c478bd9Sstevel@tonic-gate **		none.
6327c478bd9Sstevel@tonic-gate **
6337c478bd9Sstevel@tonic-gate **	Side Effects:
6347c478bd9Sstevel@tonic-gate **		Reads aliasfile into the symbol table.
6357c478bd9Sstevel@tonic-gate **		Optionally, builds the .dir & .pag files.
6367c478bd9Sstevel@tonic-gate */
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate void
readaliases(map,af,announcestats,logstats)6397c478bd9Sstevel@tonic-gate readaliases(map, af, announcestats, logstats)
6407c478bd9Sstevel@tonic-gate 	register MAP *map;
6417c478bd9Sstevel@tonic-gate 	SM_FILE_T *af;
6427c478bd9Sstevel@tonic-gate 	bool announcestats;
6437c478bd9Sstevel@tonic-gate 	bool logstats;
6447c478bd9Sstevel@tonic-gate {
6457c478bd9Sstevel@tonic-gate 	register char *p;
6467c478bd9Sstevel@tonic-gate 	char *rhs;
6477c478bd9Sstevel@tonic-gate 	bool skipping;
6487c478bd9Sstevel@tonic-gate 	long naliases, bytes, longest;
6497c478bd9Sstevel@tonic-gate 	ADDRESS al, bl;
6507c478bd9Sstevel@tonic-gate 	char line[BUFSIZ];
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 	/*
6537c478bd9Sstevel@tonic-gate 	**  Read and interpret lines
6547c478bd9Sstevel@tonic-gate 	*/
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 	FileName = map->map_file;
6577c478bd9Sstevel@tonic-gate 	LineNumber = 0;
6587c478bd9Sstevel@tonic-gate 	naliases = bytes = longest = 0;
6597c478bd9Sstevel@tonic-gate 	skipping = false;
660058561cbSjbeck 	while (sm_io_fgets(af, SM_TIME_DEFAULT, line, sizeof(line)) != NULL)
6617c478bd9Sstevel@tonic-gate 	{
6627c478bd9Sstevel@tonic-gate 		int lhssize, rhssize;
6637c478bd9Sstevel@tonic-gate 		int c;
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate 		LineNumber++;
6667c478bd9Sstevel@tonic-gate 		p = strchr(line, '\n');
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 		/* XXX what if line="a\\" ? */
6697c478bd9Sstevel@tonic-gate 		while (p != NULL && p > line && p[-1] == '\\')
6707c478bd9Sstevel@tonic-gate 		{
6717c478bd9Sstevel@tonic-gate 			p--;
6727c478bd9Sstevel@tonic-gate 			if (sm_io_fgets(af, SM_TIME_DEFAULT, p,
6737c478bd9Sstevel@tonic-gate 					SPACELEFT(line, p)) == NULL)
6747c478bd9Sstevel@tonic-gate 				break;
6757c478bd9Sstevel@tonic-gate 			LineNumber++;
6767c478bd9Sstevel@tonic-gate 			p = strchr(p, '\n');
6777c478bd9Sstevel@tonic-gate 		}
6787c478bd9Sstevel@tonic-gate 		if (p != NULL)
6797c478bd9Sstevel@tonic-gate 			*p = '\0';
6807c478bd9Sstevel@tonic-gate 		else if (!sm_io_eof(af))
6817c478bd9Sstevel@tonic-gate 		{
6827c478bd9Sstevel@tonic-gate 			errno = 0;
6837c478bd9Sstevel@tonic-gate 			syserr("554 5.3.0 alias line too long");
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate 			/* flush to end of line */
6867c478bd9Sstevel@tonic-gate 			while ((c = sm_io_getc(af, SM_TIME_DEFAULT)) !=
6877c478bd9Sstevel@tonic-gate 				SM_IO_EOF && c != '\n')
6887c478bd9Sstevel@tonic-gate 				continue;
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 			/* skip any continuation lines */
6917c478bd9Sstevel@tonic-gate 			skipping = true;
6927c478bd9Sstevel@tonic-gate 			continue;
6937c478bd9Sstevel@tonic-gate 		}
6947c478bd9Sstevel@tonic-gate 		switch (line[0])
6957c478bd9Sstevel@tonic-gate 		{
6967c478bd9Sstevel@tonic-gate 		  case '#':
6977c478bd9Sstevel@tonic-gate 		  case '\0':
6987c478bd9Sstevel@tonic-gate 			skipping = false;
6997c478bd9Sstevel@tonic-gate 			continue;
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 		  case ' ':
7027c478bd9Sstevel@tonic-gate 		  case '\t':
7037c478bd9Sstevel@tonic-gate 			if (!skipping)
7047c478bd9Sstevel@tonic-gate 				syserr("554 5.3.5 Non-continuation line starts with space");
7057c478bd9Sstevel@tonic-gate 			skipping = true;
7067c478bd9Sstevel@tonic-gate 			continue;
7077c478bd9Sstevel@tonic-gate 		}
7087c478bd9Sstevel@tonic-gate 		skipping = false;
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 		/*
7117c478bd9Sstevel@tonic-gate 		**  Process the LHS
7127c478bd9Sstevel@tonic-gate 		**	Find the colon separator, and parse the address.
7137c478bd9Sstevel@tonic-gate 		**	It should resolve to a local name -- this will
7147c478bd9Sstevel@tonic-gate 		**	be checked later (we want to optionally do
7157c478bd9Sstevel@tonic-gate 		**	parsing of the RHS first to maximize error
7167c478bd9Sstevel@tonic-gate 		**	detection).
7177c478bd9Sstevel@tonic-gate 		*/
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
7207c478bd9Sstevel@tonic-gate 			continue;
7217c478bd9Sstevel@tonic-gate 		if (*p++ != ':')
7227c478bd9Sstevel@tonic-gate 		{
7237c478bd9Sstevel@tonic-gate 			syserr("554 5.3.5 missing colon");
7247c478bd9Sstevel@tonic-gate 			continue;
7257c478bd9Sstevel@tonic-gate 		}
7267c478bd9Sstevel@tonic-gate 		if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv, true)
7277c478bd9Sstevel@tonic-gate 		    == NULL)
7287c478bd9Sstevel@tonic-gate 		{
7297c478bd9Sstevel@tonic-gate 			syserr("554 5.3.5 %.40s... illegal alias name", line);
7307c478bd9Sstevel@tonic-gate 			continue;
7317c478bd9Sstevel@tonic-gate 		}
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate 		/*
7347c478bd9Sstevel@tonic-gate 		**  Process the RHS.
7357c478bd9Sstevel@tonic-gate 		**	'al' is the internal form of the LHS address.
7367c478bd9Sstevel@tonic-gate 		**	'p' points to the text of the RHS.
7377c478bd9Sstevel@tonic-gate 		*/
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
7407c478bd9Sstevel@tonic-gate 			p++;
7417c478bd9Sstevel@tonic-gate 		rhs = p;
7427c478bd9Sstevel@tonic-gate 		for (;;)
7437c478bd9Sstevel@tonic-gate 		{
7447c478bd9Sstevel@tonic-gate 			register char *nlp;
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate 			nlp = &p[strlen(p)];
7477c478bd9Sstevel@tonic-gate 			if (nlp > p && nlp[-1] == '\n')
7487c478bd9Sstevel@tonic-gate 				*--nlp = '\0';
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 			if (CheckAliases)
7517c478bd9Sstevel@tonic-gate 			{
7527c478bd9Sstevel@tonic-gate 				/* do parsing & compression of addresses */
7537c478bd9Sstevel@tonic-gate 				while (*p != '\0')
7547c478bd9Sstevel@tonic-gate 				{
7557c478bd9Sstevel@tonic-gate 					auto char *delimptr;
7567c478bd9Sstevel@tonic-gate 
7577c478bd9Sstevel@tonic-gate 					while ((isascii(*p) && isspace(*p)) ||
7587c478bd9Sstevel@tonic-gate 								*p == ',')
7597c478bd9Sstevel@tonic-gate 						p++;
7607c478bd9Sstevel@tonic-gate 					if (*p == '\0')
7617c478bd9Sstevel@tonic-gate 						break;
7627c478bd9Sstevel@tonic-gate 					if (parseaddr(p, &bl, RF_COPYNONE, ',',
7637c478bd9Sstevel@tonic-gate 						      &delimptr, CurEnv, true)
7647c478bd9Sstevel@tonic-gate 					    == NULL)
7657c478bd9Sstevel@tonic-gate 						usrerr("553 5.3.5 %s... bad address", p);
7667c478bd9Sstevel@tonic-gate 					p = delimptr;
7677c478bd9Sstevel@tonic-gate 				}
7687c478bd9Sstevel@tonic-gate 			}
7697c478bd9Sstevel@tonic-gate 			else
7707c478bd9Sstevel@tonic-gate 			{
7717c478bd9Sstevel@tonic-gate 				p = nlp;
7727c478bd9Sstevel@tonic-gate 			}
7737c478bd9Sstevel@tonic-gate 
7747c478bd9Sstevel@tonic-gate 			/* see if there should be a continuation line */
7757c478bd9Sstevel@tonic-gate 			c = sm_io_getc(af, SM_TIME_DEFAULT);
7767c478bd9Sstevel@tonic-gate 			if (!sm_io_eof(af))
7777c478bd9Sstevel@tonic-gate 				(void) sm_io_ungetc(af, SM_TIME_DEFAULT, c);
7787c478bd9Sstevel@tonic-gate 			if (c != ' ' && c != '\t')
7797c478bd9Sstevel@tonic-gate 				break;
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate 			/* read continuation line */
7827c478bd9Sstevel@tonic-gate 			if (sm_io_fgets(af, SM_TIME_DEFAULT, p,
783058561cbSjbeck 					sizeof(line) - (p-line)) == NULL)
7847c478bd9Sstevel@tonic-gate 				break;
7857c478bd9Sstevel@tonic-gate 			LineNumber++;
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate 			/* check for line overflow */
7887c478bd9Sstevel@tonic-gate 			if (strchr(p, '\n') == NULL && !sm_io_eof(af))
7897c478bd9Sstevel@tonic-gate 			{
7907c478bd9Sstevel@tonic-gate 				usrerr("554 5.3.5 alias too long");
7917c478bd9Sstevel@tonic-gate 				while ((c = sm_io_getc(af, SM_TIME_DEFAULT))
7927c478bd9Sstevel@tonic-gate 				       != SM_IO_EOF && c != '\n')
7937c478bd9Sstevel@tonic-gate 					continue;
7947c478bd9Sstevel@tonic-gate 				skipping = true;
7957c478bd9Sstevel@tonic-gate 				break;
7967c478bd9Sstevel@tonic-gate 			}
7977c478bd9Sstevel@tonic-gate 		}
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 		if (skipping)
8007c478bd9Sstevel@tonic-gate 			continue;
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 		if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags))
8037c478bd9Sstevel@tonic-gate 		{
8047c478bd9Sstevel@tonic-gate 			syserr("554 5.3.5 %s... cannot alias non-local names",
8057c478bd9Sstevel@tonic-gate 				al.q_paddr);
8067c478bd9Sstevel@tonic-gate 			continue;
8077c478bd9Sstevel@tonic-gate 		}
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate 		/*
8107c478bd9Sstevel@tonic-gate 		**  Insert alias into symbol table or database file.
8117c478bd9Sstevel@tonic-gate 		**
8127c478bd9Sstevel@tonic-gate 		**	Special case pOStmaStER -- always make it lower case.
8137c478bd9Sstevel@tonic-gate 		*/
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate 		if (sm_strcasecmp(al.q_user, "postmaster") == 0)
8167c478bd9Sstevel@tonic-gate 			makelower(al.q_user);
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 		lhssize = strlen(al.q_user);
8197c478bd9Sstevel@tonic-gate 		rhssize = strlen(rhs);
8207c478bd9Sstevel@tonic-gate 		if (rhssize > 0)
8217c478bd9Sstevel@tonic-gate 		{
8227c478bd9Sstevel@tonic-gate 			/* is RHS empty (just spaces)? */
8237c478bd9Sstevel@tonic-gate 			p = rhs;
8247c478bd9Sstevel@tonic-gate 			while (isascii(*p) && isspace(*p))
8257c478bd9Sstevel@tonic-gate 				p++;
8267c478bd9Sstevel@tonic-gate 		}
8277c478bd9Sstevel@tonic-gate 		if (rhssize == 0 || *p == '\0')
8287c478bd9Sstevel@tonic-gate 		{
8297c478bd9Sstevel@tonic-gate 			syserr("554 5.3.5 %.40s... missing value for alias",
8307c478bd9Sstevel@tonic-gate 			       line);
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 		}
8337c478bd9Sstevel@tonic-gate 		else
8347c478bd9Sstevel@tonic-gate 		{
8357c478bd9Sstevel@tonic-gate 			map->map_class->map_store(map, al.q_user, rhs);
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 			/* statistics */
8387c478bd9Sstevel@tonic-gate 			naliases++;
8397c478bd9Sstevel@tonic-gate 			bytes += lhssize + rhssize;
8407c478bd9Sstevel@tonic-gate 			if (rhssize > longest)
8417c478bd9Sstevel@tonic-gate 				longest = rhssize;
8427c478bd9Sstevel@tonic-gate 		}
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate #if 0
8457c478bd9Sstevel@tonic-gate 	/*
8467c478bd9Sstevel@tonic-gate 	**  address strings are now stored in the envelope rpool,
8477c478bd9Sstevel@tonic-gate 	**  and therefore cannot be freed.
8487c478bd9Sstevel@tonic-gate 	*/
8497c478bd9Sstevel@tonic-gate 		if (al.q_paddr != NULL)
8507c478bd9Sstevel@tonic-gate 			sm_free(al.q_paddr);  /* disabled */
8517c478bd9Sstevel@tonic-gate 		if (al.q_host != NULL)
8527c478bd9Sstevel@tonic-gate 			sm_free(al.q_host);  /* disabled */
8537c478bd9Sstevel@tonic-gate 		if (al.q_user != NULL)
8547c478bd9Sstevel@tonic-gate 			sm_free(al.q_user);  /* disabled */
8557c478bd9Sstevel@tonic-gate #endif /* 0 */
8567c478bd9Sstevel@tonic-gate 	}
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	CurEnv->e_to = NULL;
8597c478bd9Sstevel@tonic-gate 	FileName = NULL;
8607c478bd9Sstevel@tonic-gate 	if (Verbose || announcestats)
8617c478bd9Sstevel@tonic-gate 		message("%s: %ld aliases, longest %ld bytes, %ld bytes total",
8627c478bd9Sstevel@tonic-gate 			map->map_file, naliases, longest, bytes);
8637c478bd9Sstevel@tonic-gate 	if (LogLevel > 7 && logstats)
8647c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID,
8657c478bd9Sstevel@tonic-gate 			"%s: %ld aliases, longest %ld bytes, %ld bytes total",
8667c478bd9Sstevel@tonic-gate 			map->map_file, naliases, longest, bytes);
8677c478bd9Sstevel@tonic-gate }
8687c478bd9Sstevel@tonic-gate /*
8697c478bd9Sstevel@tonic-gate **  FORWARD -- Try to forward mail
8707c478bd9Sstevel@tonic-gate **
8717c478bd9Sstevel@tonic-gate **	This is similar but not identical to aliasing.
8727c478bd9Sstevel@tonic-gate **
8737c478bd9Sstevel@tonic-gate **	Parameters:
8747c478bd9Sstevel@tonic-gate **		user -- the name of the user who's mail we would like
8757c478bd9Sstevel@tonic-gate **			to forward to.  It must have been verified --
8767c478bd9Sstevel@tonic-gate **			i.e., the q_home field must have been filled
8777c478bd9Sstevel@tonic-gate **			in.
8787c478bd9Sstevel@tonic-gate **		sendq -- a pointer to the head of the send queue to
8797c478bd9Sstevel@tonic-gate **			put this user's aliases in.
8807c478bd9Sstevel@tonic-gate **		aliaslevel -- the current alias nesting depth.
8817c478bd9Sstevel@tonic-gate **		e -- the current envelope.
8827c478bd9Sstevel@tonic-gate **
8837c478bd9Sstevel@tonic-gate **	Returns:
8847c478bd9Sstevel@tonic-gate **		none.
8857c478bd9Sstevel@tonic-gate **
8867c478bd9Sstevel@tonic-gate **	Side Effects:
8877c478bd9Sstevel@tonic-gate **		New names are added to send queues.
8887c478bd9Sstevel@tonic-gate */
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate void
forward(user,sendq,aliaslevel,e)8917c478bd9Sstevel@tonic-gate forward(user, sendq, aliaslevel, e)
8927c478bd9Sstevel@tonic-gate 	ADDRESS *user;
8937c478bd9Sstevel@tonic-gate 	ADDRESS **sendq;
8947c478bd9Sstevel@tonic-gate 	int aliaslevel;
8957c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
8967c478bd9Sstevel@tonic-gate {
8977c478bd9Sstevel@tonic-gate 	char *pp;
8987c478bd9Sstevel@tonic-gate 	char *ep;
8997c478bd9Sstevel@tonic-gate 	bool got_transient;
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 	if (tTd(27, 1))
9027c478bd9Sstevel@tonic-gate 		sm_dprintf("forward(%s)\n", user->q_paddr);
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate 	if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
9057c478bd9Sstevel@tonic-gate 	    !QS_IS_OK(user->q_state))
9067c478bd9Sstevel@tonic-gate 		return;
9077c478bd9Sstevel@tonic-gate 	if (ForwardPath != NULL && *ForwardPath == '\0')
9087c478bd9Sstevel@tonic-gate 		return;
9097c478bd9Sstevel@tonic-gate 	if (user->q_home == NULL)
9107c478bd9Sstevel@tonic-gate 	{
9117c478bd9Sstevel@tonic-gate 		syserr("554 5.3.0 forward: no home");
9127c478bd9Sstevel@tonic-gate 		user->q_home = "/no/such/directory";
9137c478bd9Sstevel@tonic-gate 	}
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	/* good address -- look for .forward file in home */
9167c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'z', user->q_home);
9177c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'u', user->q_user);
9187c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'h', user->q_host);
9197c478bd9Sstevel@tonic-gate 	if (ForwardPath == NULL)
9207c478bd9Sstevel@tonic-gate 		ForwardPath = newstr("\201z/.forward");
9217c478bd9Sstevel@tonic-gate 
9227c478bd9Sstevel@tonic-gate 	got_transient = false;
9237c478bd9Sstevel@tonic-gate 	for (pp = ForwardPath; pp != NULL; pp = ep)
9247c478bd9Sstevel@tonic-gate 	{
9257c478bd9Sstevel@tonic-gate 		int err;
9267c478bd9Sstevel@tonic-gate 		char buf[MAXPATHLEN];
9277c478bd9Sstevel@tonic-gate 		struct stat st;
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate 		ep = strchr(pp, SEPARATOR);
9307c478bd9Sstevel@tonic-gate 		if (ep != NULL)
9317c478bd9Sstevel@tonic-gate 			*ep = '\0';
932058561cbSjbeck 		expand(pp, buf, sizeof(buf), e);
9337c478bd9Sstevel@tonic-gate 		if (ep != NULL)
9347c478bd9Sstevel@tonic-gate 			*ep++ = SEPARATOR;
9357c478bd9Sstevel@tonic-gate 		if (buf[0] == '\0')
9367c478bd9Sstevel@tonic-gate 			continue;
9377c478bd9Sstevel@tonic-gate 		if (tTd(27, 3))
9387c478bd9Sstevel@tonic-gate 			sm_dprintf("forward: trying %s\n", buf);
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate 		err = include(buf, true, user, sendq, aliaslevel, e);
9417c478bd9Sstevel@tonic-gate 		if (err == 0)
9427c478bd9Sstevel@tonic-gate 			break;
9437c478bd9Sstevel@tonic-gate 		else if (transienterror(err))
9447c478bd9Sstevel@tonic-gate 		{
9457c478bd9Sstevel@tonic-gate 			/* we may have to suspend this message */
9467c478bd9Sstevel@tonic-gate 			got_transient = true;
9477c478bd9Sstevel@tonic-gate 			if (tTd(27, 2))
9487c478bd9Sstevel@tonic-gate 				sm_dprintf("forward: transient error on %s\n",
9497c478bd9Sstevel@tonic-gate 					   buf);
9507c478bd9Sstevel@tonic-gate 			if (LogLevel > 2)
9517c478bd9Sstevel@tonic-gate 			{
9527c478bd9Sstevel@tonic-gate 				char *curhost = CurHostName;
9537c478bd9Sstevel@tonic-gate 
9547c478bd9Sstevel@tonic-gate 				CurHostName = NULL;
9557c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
9567c478bd9Sstevel@tonic-gate 					  "forward %s: transient error: %s",
9577c478bd9Sstevel@tonic-gate 					  buf, sm_errstring(err));
9587c478bd9Sstevel@tonic-gate 				CurHostName = curhost;
9597c478bd9Sstevel@tonic-gate 			}
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 		}
9627c478bd9Sstevel@tonic-gate 		else
9637c478bd9Sstevel@tonic-gate 		{
9647c478bd9Sstevel@tonic-gate 			switch (err)
9657c478bd9Sstevel@tonic-gate 			{
9667c478bd9Sstevel@tonic-gate 			  case ENOENT:
9677c478bd9Sstevel@tonic-gate 				break;
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate 			  case E_SM_WWDIR:
9707c478bd9Sstevel@tonic-gate 			  case E_SM_GWDIR:
9717c478bd9Sstevel@tonic-gate 				/* check if it even exists */
9727c478bd9Sstevel@tonic-gate 				if (stat(buf, &st) < 0 && errno == ENOENT)
9737c478bd9Sstevel@tonic-gate 				{
9747c478bd9Sstevel@tonic-gate 					if (bitnset(DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH,
9757c478bd9Sstevel@tonic-gate 						    DontBlameSendmail))
9767c478bd9Sstevel@tonic-gate 						break;
9777c478bd9Sstevel@tonic-gate 				}
9787c478bd9Sstevel@tonic-gate 				/* FALLTHROUGH */
9797c478bd9Sstevel@tonic-gate 
9807c478bd9Sstevel@tonic-gate #if _FFR_FORWARD_SYSERR
9817c478bd9Sstevel@tonic-gate 			  case E_SM_NOSLINK:
9827c478bd9Sstevel@tonic-gate 			  case E_SM_NOHLINK:
9837c478bd9Sstevel@tonic-gate 			  case E_SM_REGONLY:
9847c478bd9Sstevel@tonic-gate 			  case E_SM_ISEXEC:
9857c478bd9Sstevel@tonic-gate 			  case E_SM_WWFILE:
9867c478bd9Sstevel@tonic-gate 			  case E_SM_GWFILE:
9877c478bd9Sstevel@tonic-gate 				syserr("forward: %s: %s", buf, sm_errstring(err));
9887c478bd9Sstevel@tonic-gate 				break;
9897c478bd9Sstevel@tonic-gate #endif /* _FFR_FORWARD_SYSERR */
9907c478bd9Sstevel@tonic-gate 
991*fec46055SToomas Soome 				/* FALLTHROUGH */
9927c478bd9Sstevel@tonic-gate 			  default:
9937c478bd9Sstevel@tonic-gate 				if (LogLevel > (RunAsUid == 0 ? 2 : 10))
9947c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_WARNING, e->e_id,
9957c478bd9Sstevel@tonic-gate 						  "forward %s: %s", buf,
9967c478bd9Sstevel@tonic-gate 						  sm_errstring(err));
9977c478bd9Sstevel@tonic-gate 				if (Verbose)
9987c478bd9Sstevel@tonic-gate 					message("forward: %s: %s",
9997c478bd9Sstevel@tonic-gate 						buf, sm_errstring(err));
10007c478bd9Sstevel@tonic-gate 				break;
10017c478bd9Sstevel@tonic-gate 			}
10027c478bd9Sstevel@tonic-gate 		}
10037c478bd9Sstevel@tonic-gate 	}
10047c478bd9Sstevel@tonic-gate 	if (pp == NULL && got_transient)
10057c478bd9Sstevel@tonic-gate 	{
10067c478bd9Sstevel@tonic-gate 		/*
10077c478bd9Sstevel@tonic-gate 		**  There was no successful .forward open and at least one
10087c478bd9Sstevel@tonic-gate 		**  transient open.  We have to defer this address for
10097c478bd9Sstevel@tonic-gate 		**  further delivery.
10107c478bd9Sstevel@tonic-gate 		*/
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate 		message("transient .forward open error: message queued");
10137c478bd9Sstevel@tonic-gate 		user->q_state = QS_QUEUEUP;
10147c478bd9Sstevel@tonic-gate 		return;
10157c478bd9Sstevel@tonic-gate 	}
10167c478bd9Sstevel@tonic-gate }
1017