xref: /freebsd/contrib/sendmail/src/alias.c (revision 11afcc8f9f96d657b8e6f7547c02c1957331fc96)
1 /*
2  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4  *
5  * By using this file, you agree to the terms and conditions set
6  * forth in the LICENSE file which can be found at the top level of
7  * the sendmail distribution.
8  *
9  * Copyright (c) 1988, 1993
10  *	The Regents of the University of California.  All rights reserved.
11  */
12 
13 # include "sendmail.h"
14 
15 #ifndef lint
16 static char sccsid[] = "@(#)alias.c	8.92 (Berkeley) 6/5/98";
17 #endif /* not lint */
18 
19 
20 MAP	*AliasFileMap = NULL;	/* the actual aliases.files map */
21 int	NAliasFileMaps;		/* the number of entries in AliasFileMap */
22 /*
23 **  ALIAS -- Compute aliases.
24 **
25 **	Scans the alias file for an alias for the given address.
26 **	If found, it arranges to deliver to the alias list instead.
27 **	Uses libdbm database if -DDBM.
28 **
29 **	Parameters:
30 **		a -- address to alias.
31 **		sendq -- a pointer to the head of the send queue
32 **			to put the aliases in.
33 **		aliaslevel -- the current alias nesting depth.
34 **		e -- the current envelope.
35 **
36 **	Returns:
37 **		none
38 **
39 **	Side Effects:
40 **		Aliases found are expanded.
41 **
42 **	Deficiencies:
43 **		It should complain about names that are aliased to
44 **			nothing.
45 */
46 
47 void
48 alias(a, sendq, aliaslevel, e)
49 	register ADDRESS *a;
50 	ADDRESS **sendq;
51 	int aliaslevel;
52 	register ENVELOPE *e;
53 {
54 	register char *p;
55 	char *owner;
56 	auto int stat = EX_OK;
57 	char obuf[MAXNAME + 7];
58 	extern char *aliaslookup __P((char *, int *, ENVELOPE *));
59 
60 	if (tTd(27, 1))
61 		printf("alias(%s)\n", a->q_user);
62 
63 	/* don't realias already aliased names */
64 	if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags))
65 		return;
66 
67 	if (NoAlias)
68 		return;
69 
70 	e->e_to = a->q_paddr;
71 
72 	/*
73 	**  Look up this name.
74 	**
75 	**	If the map was unavailable, we will queue this message
76 	**	until the map becomes available; otherwise, we could
77 	**	bounce messages inappropriately.
78 	*/
79 
80 	p = aliaslookup(a->q_user, &stat, e);
81 	if (stat == EX_TEMPFAIL || stat == EX_UNAVAILABLE)
82 	{
83 		a->q_flags |= QQUEUEUP;
84 		if (e->e_message == NULL)
85 			e->e_message = newstr("alias database unavailable");
86 		return;
87 	}
88 	if (p == NULL)
89 		return;
90 
91 	/*
92 	**  Match on Alias.
93 	**	Deliver to the target list.
94 	*/
95 
96 	if (tTd(27, 1))
97 		printf("%s (%s, %s) aliased to %s\n",
98 		    a->q_paddr, a->q_host, a->q_user, p);
99 	if (bitset(EF_VRFYONLY, e->e_flags))
100 	{
101 		a->q_flags |= QVERIFIED;
102 		return;
103 	}
104 	message("aliased to %s", shortenstring(p, MAXSHORTSTR));
105 	if (LogLevel > 9)
106 		sm_syslog(LOG_INFO, e->e_id,
107 			"alias %.100s => %s",
108 			a->q_paddr, shortenstring(p, MAXSHORTSTR));
109 	a->q_flags &= ~QSELFREF;
110 	if (tTd(27, 5))
111 	{
112 		printf("alias: QDONTSEND ");
113 		printaddr(a, FALSE);
114 	}
115 	a->q_flags |= QDONTSEND;
116 	(void) sendtolist(p, a, sendq, aliaslevel + 1, e);
117 	if (bitset(QSELFREF, a->q_flags))
118 		a->q_flags &= ~QDONTSEND;
119 
120 	/*
121 	**  Look for owner of alias
122 	*/
123 
124 	(void) strcpy(obuf, "owner-");
125 	if (strncmp(a->q_user, "owner-", 6) == 0 ||
126 	    strlen(a->q_user) > (SIZE_T) sizeof obuf - 7)
127 		(void) strcat(obuf, "owner");
128 	else
129 		(void) strcat(obuf, a->q_user);
130 	owner = aliaslookup(obuf, &stat, e);
131 	if (owner == NULL)
132 		return;
133 
134 	/* reflect owner into envelope sender */
135 	if (strpbrk(owner, ",:/|\"") != NULL)
136 		owner = obuf;
137 	a->q_owner = newstr(owner);
138 
139 	/* announce delivery to this alias; NORECEIPT bit set later */
140 	if (e->e_xfp != NULL)
141 		fprintf(e->e_xfp, "Message delivered to mailing list %s\n",
142 			a->q_paddr);
143 	e->e_flags |= EF_SENDRECEIPT;
144 	a->q_flags |= QDELIVERED|QEXPANDED;
145 }
146 /*
147 **  ALIASLOOKUP -- look up a name in the alias file.
148 **
149 **	Parameters:
150 **		name -- the name to look up.
151 **		pstat -- a pointer to a place to put the status.
152 **		e -- the current envelope.
153 **
154 **	Returns:
155 **		the value of name.
156 **		NULL if unknown.
157 **
158 **	Side Effects:
159 **		none.
160 **
161 **	Warnings:
162 **		The return value will be trashed across calls.
163 */
164 
165 char *
166 aliaslookup(name, pstat, e)
167 	char *name;
168 	int *pstat;
169 	ENVELOPE *e;
170 {
171 	static MAP *map = NULL;
172 
173 	if (map == NULL)
174 	{
175 		STAB *s = stab("aliases", ST_MAP, ST_FIND);
176 
177 		if (s == NULL)
178 			return NULL;
179 		map = &s->s_map;
180 	}
181 	if (!bitset(MF_OPEN, map->map_mflags))
182 		return NULL;
183 
184 	/* special case POstMastER -- always use lower case */
185 	if (strcasecmp(name, "postmaster") == 0)
186 		name = "postmaster";
187 
188 	return (*map->map_class->map_lookup)(map, name, NULL, pstat);
189 }
190 /*
191 **  SETALIAS -- set up an alias map
192 **
193 **	Called when reading configuration file.
194 **
195 **	Parameters:
196 **		spec -- the alias specification
197 **
198 **	Returns:
199 **		none.
200 */
201 
202 void
203 setalias(spec)
204 	char *spec;
205 {
206 	register char *p;
207 	register MAP *map;
208 	char *class;
209 	STAB *s;
210 
211 	if (tTd(27, 8))
212 		printf("setalias(%s)\n", spec);
213 
214 	for (p = spec; p != NULL; )
215 	{
216 		char buf[50];
217 
218 		while (isascii(*p) && isspace(*p))
219 			p++;
220 		if (*p == '\0')
221 			break;
222 		spec = p;
223 
224 		if (NAliasFileMaps >= MAXMAPSTACK)
225 		{
226 			syserr("Too many alias databases defined, %d max",
227 				MAXMAPSTACK);
228 			return;
229 		}
230 		if (AliasFileMap == NULL)
231 		{
232 			strcpy(buf, "aliases.files sequence");
233 			AliasFileMap = makemapentry(buf);
234 			if (AliasFileMap == NULL)
235 			{
236 				syserr("setalias: cannot create aliases.files map");
237 				return;
238 			}
239 		}
240 		(void) snprintf(buf, sizeof buf, "Alias%d", NAliasFileMaps);
241 		s = stab(buf, ST_MAP, ST_ENTER);
242 		map = &s->s_map;
243 		bzero(map, sizeof *map);
244 		map->map_mname = s->s_name;
245 
246 		p = strpbrk(p, " ,/:");
247 		if (p != NULL && *p == ':')
248 		{
249 			/* map name */
250 			*p++ = '\0';
251 			class = spec;
252 			spec = p;
253 		}
254 		else
255 		{
256 			class = "implicit";
257 			map->map_mflags = MF_INCLNULL;
258 		}
259 
260 		/* find end of spec */
261 		if (p != NULL)
262 			p = strchr(p, ',');
263 		if (p != NULL)
264 			*p++ = '\0';
265 
266 		if (tTd(27, 20))
267 			printf("  map %s:%s %s\n", class, s->s_name, spec);
268 
269 		/* look up class */
270 		s = stab(class, ST_MAPCLASS, ST_FIND);
271 		if (s == NULL)
272 		{
273 			syserr("setalias: unknown alias class %s", class);
274 		}
275 		else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
276 		{
277 			syserr("setalias: map class %s can't handle aliases",
278 				class);
279 		}
280 		else
281 		{
282 			map->map_class = &s->s_mapclass;
283 			if (map->map_class->map_parse(map, spec))
284 			{
285 				map->map_mflags |= MF_VALID|MF_ALIAS;
286 				AliasFileMap->map_stack[NAliasFileMaps++] = map;
287 			}
288 		}
289 	}
290 }
291 /*
292 **  ALIASWAIT -- wait for distinguished @:@ token to appear.
293 **
294 **	This can decide to reopen or rebuild the alias file
295 **
296 **	Parameters:
297 **		map -- a pointer to the map descriptor for this alias file.
298 **		ext -- the filename extension (e.g., ".db") for the
299 **			database file.
300 **		isopen -- if set, the database is already open, and we
301 **			should check for validity; otherwise, we are
302 **			just checking to see if it should be created.
303 **
304 **	Returns:
305 **		TRUE -- if the database is open when we return.
306 **		FALSE -- if the database is closed when we return.
307 */
308 
309 bool
310 aliaswait(map, ext, isopen)
311 	MAP *map;
312 	char *ext;
313 	int isopen;
314 {
315 	bool attimeout = FALSE;
316 	time_t mtime;
317 	struct stat stb;
318 	char buf[MAXNAME + 1];
319 
320 	if (tTd(27, 3))
321 		printf("aliaswait(%s:%s)\n",
322 			map->map_class->map_cname, map->map_file);
323 	if (bitset(MF_ALIASWAIT, map->map_mflags))
324 		return isopen;
325 	map->map_mflags |= MF_ALIASWAIT;
326 
327 	if (SafeAlias > 0)
328 	{
329 		auto int st;
330 		time_t toolong = curtime() + SafeAlias;
331 		unsigned int sleeptime = 2;
332 
333 		while (isopen &&
334 		       map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
335 		{
336 			if (curtime() > toolong)
337 			{
338 				/* we timed out */
339 				attimeout = TRUE;
340 				break;
341 			}
342 
343 			/*
344 			**  Close and re-open the alias database in case
345 			**  the one is mv'ed instead of cp'ed in.
346 			*/
347 
348 			if (tTd(27, 2))
349 				printf("aliaswait: sleeping for %d seconds\n",
350 					sleeptime);
351 
352 			map->map_class->map_close(map);
353 			map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
354 			sleep(sleeptime);
355 			sleeptime *= 2;
356 			if (sleeptime > 60)
357 				sleeptime = 60;
358 			isopen = map->map_class->map_open(map, O_RDONLY);
359 		}
360 	}
361 
362 	/* see if we need to go into auto-rebuild mode */
363 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
364 	{
365 		if (tTd(27, 3))
366 			printf("aliaswait: not rebuildable\n");
367 		map->map_mflags &= ~MF_ALIASWAIT;
368 		return isopen;
369 	}
370 	if (stat(map->map_file, &stb) < 0)
371 	{
372 		if (tTd(27, 3))
373 			printf("aliaswait: no source file\n");
374 		map->map_mflags &= ~MF_ALIASWAIT;
375 		return isopen;
376 	}
377 	mtime = stb.st_mtime;
378 	snprintf(buf, sizeof buf, "%s%s",
379 		map->map_file, ext == NULL ? "" : ext);
380 	if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout)
381 	{
382 		/* database is out of date */
383 		if (AutoRebuild && stb.st_ino != 0 &&
384 		    (stb.st_uid == geteuid() ||
385 		     (geteuid() == 0 && stb.st_uid == TrustedFileUid)))
386 		{
387 			bool oldSuprErrs;
388 
389 			message("auto-rebuilding alias database %s", buf);
390 			oldSuprErrs = SuprErrs;
391 			SuprErrs = TRUE;
392 			if (isopen)
393 			{
394 				map->map_class->map_close(map);
395 				map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
396 			}
397 			(void) rebuildaliases(map, TRUE);
398 			isopen = map->map_class->map_open(map, O_RDONLY);
399 			SuprErrs = oldSuprErrs;
400 		}
401 		else
402 		{
403 			if (LogLevel > 3)
404 				sm_syslog(LOG_INFO, NOQID,
405 					"alias database %s out of date",
406 					buf);
407 			message("Warning: alias database %s out of date", buf);
408 		}
409 	}
410 	map->map_mflags &= ~MF_ALIASWAIT;
411 	return isopen;
412 }
413 /*
414 **  REBUILDALIASES -- rebuild the alias database.
415 **
416 **	Parameters:
417 **		map -- the database to rebuild.
418 **		automatic -- set if this was automatically generated.
419 **
420 **	Returns:
421 **		TRUE if successful; FALSE otherwise.
422 **
423 **	Side Effects:
424 **		Reads the text version of the database, builds the
425 **		DBM or DB version.
426 */
427 
428 bool
429 rebuildaliases(map, automatic)
430 	register MAP *map;
431 	bool automatic;
432 {
433 	FILE *af;
434 	bool nolock = FALSE;
435 	bool success = FALSE;
436 	int sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK;
437 	sigfunc_t oldsigint, oldsigquit;
438 #ifdef SIGTSTP
439 	sigfunc_t oldsigtstp;
440 #endif
441 
442 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
443 		return FALSE;
444 
445 	if (!bitset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail))
446 		sff |= SFF_NOWLINK;
447 	if (!bitset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail))
448 		sff |= SFF_NOGWFILES;
449 	if (!bitset(DBS_WORLDWRITABLEALIASFILE, DontBlameSendmail))
450 		sff |= SFF_NOWWFILES;
451 
452 	/* try to lock the source file */
453 	if ((af = safefopen(map->map_file, O_RDWR, 0, sff)) == NULL)
454 	{
455 		struct stat stb;
456 
457 		if ((errno != EACCES && errno != EROFS) || automatic ||
458 		    (af = safefopen(map->map_file, O_RDONLY, 0, sff)) == NULL)
459 		{
460 			int saveerr = errno;
461 
462 			if (tTd(27, 1))
463 				printf("Can't open %s: %s\n",
464 					map->map_file, errstring(saveerr));
465 			if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags))
466 				message("newaliases: cannot open %s: %s",
467 					map->map_file, errstring(saveerr));
468 			errno = 0;
469 			return FALSE;
470 		}
471 		nolock = TRUE;
472 		if (tTd(27, 1) ||
473 		    fstat(fileno(af), &stb) < 0 ||
474 		    bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode))
475 			message("warning: cannot lock %s: %s",
476 				map->map_file, errstring(errno));
477 	}
478 
479 	/* see if someone else is rebuilding the alias file */
480 	if (!nolock &&
481 	    !lockfile(fileno(af), map->map_file, NULL, LOCK_EX|LOCK_NB))
482 	{
483 		/* yes, they are -- wait until done */
484 		message("Alias file %s is locked (maybe being rebuilt)",
485 			map->map_file);
486 		if (OpMode != MD_INITALIAS)
487 		{
488 			/* wait for other rebuild to complete */
489 			(void) lockfile(fileno(af), map->map_file, NULL,
490 					LOCK_EX);
491 		}
492 		(void) xfclose(af, "rebuildaliases1", map->map_file);
493 		errno = 0;
494 		return FALSE;
495 	}
496 
497 	oldsigint = setsignal(SIGINT, SIG_IGN);
498 	oldsigquit = setsignal(SIGQUIT, SIG_IGN);
499 #ifdef SIGTSTP
500 	oldsigtstp = setsignal(SIGTSTP, SIG_IGN);
501 #endif
502 
503 	if (map->map_class->map_open(map, O_RDWR))
504 	{
505 		if (LogLevel > 7)
506 		{
507 			sm_syslog(LOG_NOTICE, NOQID,
508 				"alias database %s %srebuilt by %s",
509 				map->map_file, automatic ? "auto" : "",
510 				username());
511 		}
512 		map->map_mflags |= MF_OPEN|MF_WRITABLE;
513 		readaliases(map, af, !automatic, TRUE);
514 		success = TRUE;
515 	}
516 	else
517 	{
518 		if (tTd(27, 1))
519 			printf("Can't create database for %s: %s\n",
520 				map->map_file, errstring(errno));
521 		if (!automatic)
522 			syserr("Cannot create database for alias file %s",
523 				map->map_file);
524 	}
525 
526 	/* close the file, thus releasing locks */
527 	xfclose(af, "rebuildaliases2", map->map_file);
528 
529 	/* add distinguished entries and close the database */
530 	if (bitset(MF_OPEN, map->map_mflags))
531 	{
532 		map->map_class->map_close(map);
533 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
534 	}
535 
536 	/* restore the old signals */
537 	(void) setsignal(SIGINT, oldsigint);
538 	(void) setsignal(SIGQUIT, oldsigquit);
539 #ifdef SIGTSTP
540 	(void) setsignal(SIGTSTP, oldsigtstp);
541 #endif
542 	return success;
543 }
544 /*
545 **  READALIASES -- read and process the alias file.
546 **
547 **	This routine implements the part of initaliases that occurs
548 **	when we are not going to use the DBM stuff.
549 **
550 **	Parameters:
551 **		map -- the alias database descriptor.
552 **		af -- file to read the aliases from.
553 **		announcestats -- anounce statistics regarding number of
554 **			aliases, longest alias, etc.
555 **		logstats -- lot the same info.
556 **
557 **	Returns:
558 **		none.
559 **
560 **	Side Effects:
561 **		Reads aliasfile into the symbol table.
562 **		Optionally, builds the .dir & .pag files.
563 */
564 
565 void
566 readaliases(map, af, announcestats, logstats)
567 	register MAP *map;
568 	FILE *af;
569 	bool announcestats;
570 	bool logstats;
571 {
572 	register char *p;
573 	char *rhs;
574 	bool skipping;
575 	long naliases, bytes, longest;
576 	ADDRESS al, bl;
577 	char line[BUFSIZ];
578 
579 	/*
580 	**  Read and interpret lines
581 	*/
582 
583 	FileName = map->map_file;
584 	LineNumber = 0;
585 	naliases = bytes = longest = 0;
586 	skipping = FALSE;
587 	while (fgets(line, sizeof (line), af) != NULL)
588 	{
589 		int lhssize, rhssize;
590 		int c;
591 
592 		LineNumber++;
593 		p = strchr(line, '\n');
594 #if _FFR_BACKSLASH_IN_ALIASES
595 		while (p != NULL && p > line && p[-1] == '\\')
596 		{
597 			p--;
598 			if (fgets(p, SPACELEFT(line, p), af) == NULL)
599 				break;
600 			LineNumber++;
601 			p = strchr(p, '\n');
602 		}
603 #endif
604 		if (p != NULL)
605 			*p = '\0';
606 		else if (!feof(af))
607 		{
608 			syserr("554 alias line too long");
609 
610 			/* flush to end of line */
611 			while ((c = getc(af)) != EOF && c != '\n')
612 				continue;
613 
614 			/* skip any continuation lines */
615 			skipping = TRUE;
616 			continue;
617 		}
618 		switch (line[0])
619 		{
620 		  case '#':
621 		  case '\0':
622 			skipping = FALSE;
623 			continue;
624 
625 		  case ' ':
626 		  case '\t':
627 			if (!skipping)
628 				syserr("554 Non-continuation line starts with space");
629 			skipping = TRUE;
630 			continue;
631 		}
632 		skipping = FALSE;
633 
634 		/*
635 		**  Process the LHS
636 		**	Find the colon separator, and parse the address.
637 		**	It should resolve to a local name -- this will
638 		**	be checked later (we want to optionally do
639 		**	parsing of the RHS first to maximize error
640 		**	detection).
641 		*/
642 
643 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
644 			continue;
645 		if (*p++ != ':')
646 		{
647 			syserr("554 missing colon");
648 			continue;
649 		}
650 		if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv) == NULL)
651 		{
652 			syserr("554 %.40s... illegal alias name", line);
653 			continue;
654 		}
655 
656 		/*
657 		**  Process the RHS.
658 		**	'al' is the internal form of the LHS address.
659 		**	'p' points to the text of the RHS.
660 		*/
661 
662 		while (isascii(*p) && isspace(*p))
663 			p++;
664 		rhs = p;
665 		for (;;)
666 		{
667 			register char *nlp;
668 
669 			nlp = &p[strlen(p)];
670 			if (nlp[-1] == '\n')
671 				*--nlp = '\0';
672 
673 			if (CheckAliases)
674 			{
675 				/* do parsing & compression of addresses */
676 				while (*p != '\0')
677 				{
678 					auto char *delimptr;
679 
680 					while ((isascii(*p) && isspace(*p)) ||
681 								*p == ',')
682 						p++;
683 					if (*p == '\0')
684 						break;
685 					if (parseaddr(p, &bl, RF_COPYNONE, ',',
686 						      &delimptr, CurEnv) == NULL)
687 						usrerr("553 %s... bad address", p);
688 					p = delimptr;
689 				}
690 			}
691 			else
692 			{
693 				p = nlp;
694 			}
695 
696 			/* see if there should be a continuation line */
697 			c = getc(af);
698 			if (!feof(af))
699 				(void) ungetc(c, af);
700 			if (c != ' ' && c != '\t')
701 				break;
702 
703 			/* read continuation line */
704 			if (fgets(p, sizeof line - (p - line), af) == NULL)
705 				break;
706 			LineNumber++;
707 
708 			/* check for line overflow */
709 			if (strchr(p, '\n') == NULL && !feof(af))
710 			{
711 				usrerr("554 alias too long");
712 				while ((c = fgetc(af)) != EOF && c != '\n')
713 					continue;
714 				skipping = TRUE;
715 				break;
716 			}
717 		}
718 
719 		if (skipping)
720 			continue;
721 
722 		if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags))
723 		{
724 			syserr("554 %s... cannot alias non-local names",
725 				al.q_paddr);
726 			continue;
727 		}
728 
729 		/*
730 		**  Insert alias into symbol table or database file.
731 		**
732 		**	Special case pOStmaStER -- always make it lower case.
733 		*/
734 
735 		if (strcasecmp(al.q_user, "postmaster") == 0)
736 			makelower(al.q_user);
737 
738 		lhssize = strlen(al.q_user);
739 		rhssize = strlen(rhs);
740 		map->map_class->map_store(map, al.q_user, rhs);
741 
742 		if (al.q_paddr != NULL)
743 			free(al.q_paddr);
744 		if (al.q_host != NULL)
745 			free(al.q_host);
746 		if (al.q_user != NULL)
747 			free(al.q_user);
748 
749 		/* statistics */
750 		naliases++;
751 		bytes += lhssize + rhssize;
752 		if (rhssize > longest)
753 			longest = rhssize;
754 	}
755 
756 	CurEnv->e_to = NULL;
757 	FileName = NULL;
758 	if (Verbose || announcestats)
759 		message("%s: %d aliases, longest %d bytes, %d bytes total",
760 			map->map_file, naliases, longest, bytes);
761 	if (LogLevel > 7 && logstats)
762 		sm_syslog(LOG_INFO, NOQID,
763 			"%s: %d aliases, longest %d bytes, %d bytes total",
764 			map->map_file, naliases, longest, bytes);
765 }
766 /*
767 **  FORWARD -- Try to forward mail
768 **
769 **	This is similar but not identical to aliasing.
770 **
771 **	Parameters:
772 **		user -- the name of the user who's mail we would like
773 **			to forward to.  It must have been verified --
774 **			i.e., the q_home field must have been filled
775 **			in.
776 **		sendq -- a pointer to the head of the send queue to
777 **			put this user's aliases in.
778 **		aliaslevel -- the current alias nesting depth.
779 **		e -- the current envelope.
780 **
781 **	Returns:
782 **		none.
783 **
784 **	Side Effects:
785 **		New names are added to send queues.
786 */
787 
788 void
789 forward(user, sendq, aliaslevel, e)
790 	ADDRESS *user;
791 	ADDRESS **sendq;
792 	int aliaslevel;
793 	register ENVELOPE *e;
794 {
795 	char *pp;
796 	char *ep;
797 	bool got_transient;
798 
799 	if (tTd(27, 1))
800 		printf("forward(%s)\n", user->q_paddr);
801 
802 	if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
803 	    bitset(QBADADDR, user->q_flags))
804 		return;
805 	if (user->q_home == NULL)
806 	{
807 		syserr("554 forward: no home");
808 		user->q_home = "/no/such/directory";
809 	}
810 
811 	/* good address -- look for .forward file in home */
812 	define('z', user->q_home, e);
813 	define('u', user->q_user, e);
814 	define('h', user->q_host, e);
815 	if (ForwardPath == NULL)
816 		ForwardPath = newstr("\201z/.forward");
817 
818 	got_transient = FALSE;
819 	for (pp = ForwardPath; pp != NULL; pp = ep)
820 	{
821 		int err;
822 		char buf[MAXPATHLEN+1];
823 
824 		ep = strchr(pp, ':');
825 		if (ep != NULL)
826 			*ep = '\0';
827 		expand(pp, buf, sizeof buf, e);
828 		if (ep != NULL)
829 			*ep++ = ':';
830 		if (buf[0] == '\0')
831 			continue;
832 		if (tTd(27, 3))
833 			printf("forward: trying %s\n", buf);
834 
835 		err = include(buf, TRUE, user, sendq, aliaslevel, e);
836 		if (err == 0)
837 			break;
838 		else if (transienterror(err))
839 		{
840 			/* we may have to suspend this message */
841 			got_transient = TRUE;
842 			if (tTd(27, 2))
843 				printf("forward: transient error on %s\n", buf);
844 			if (LogLevel > 2)
845 				sm_syslog(LOG_ERR, e->e_id,
846 					"forward %s: transient error: %s",
847 					buf, errstring(err));
848 		}
849 		else
850 		{
851 			switch (err)
852 			{
853 			  case ENOENT:
854 				break;
855 
856 #if _FFR_FORWARD_SYSERR
857 			  case E_SM_NOSLINK:
858 			  case E_SM_NOHLINK:
859 			  case E_SM_REGONLY:
860 			  case E_SM_ISEXEC:
861 			  case E_SM_WWDIR:
862 			  case E_SM_GWDIR:
863 			  case E_SM_WWFILE:
864 			  case E_SM_GWFILE:
865 				syserr("forward: %s: %s", buf, errstring(err));
866 				break;
867 #endif
868 
869 			  default:
870 				if (LogLevel > (RunAsUid == 0 ? 2 : 10))
871 					sm_syslog(LOG_WARNING, e->e_id,
872 						"forward %s: %s", buf,
873 						errstring(err));
874 				if (Verbose)
875 					message("forward: %s: %s",
876 						buf,
877 						errstring(err));
878 				break;
879 			}
880 		}
881 	}
882 	if (pp == NULL && got_transient)
883 	{
884 		/*
885 		**  There was no successful .forward open and at least one
886 		**  transient open.  We have to defer this address for
887 		**  further delivery.
888 		*/
889 
890 		message("transient .forward open error: message queued");
891 		user->q_flags |= QQUEUEUP;
892 		return;
893 	}
894 }
895