xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 57438)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 # include <sys/types.h>
10 # include <sys/stat.h>
11 # include <signal.h>
12 # include "sendmail.h"
13 # include <sys/file.h>
14 # include <pwd.h>
15 # ifdef LOCKF
16 # include <fcntl.h>
17 # endif
18 
19 # ifdef DBM
20 ERROR: DBM is no longer supported -- use NDBM instead.
21 # endif
22 
23 # ifdef NEWDB
24 # include <db.h>
25 # endif
26 
27 # ifdef NDBM
28 # include <ndbm.h>
29 # endif
30 
31 #ifndef lint
32 #ifdef NEWDB
33 static char sccsid[] = "@(#)alias.c	6.3 (Berkeley) 01/09/93 (with NEWDB)";
34 #else
35 #ifdef NDBM
36 static char sccsid[] = "@(#)alias.c	6.3 (Berkeley) 01/09/93 (with NDBM)";
37 #else
38 static char sccsid[] = "@(#)alias.c	6.3 (Berkeley) 01/09/93 (without NDBM)";
39 #endif
40 #endif
41 #endif /* not lint */
42 /*
43 **  ALIAS -- Compute aliases.
44 **
45 **	Scans the alias file for an alias for the given address.
46 **	If found, it arranges to deliver to the alias list instead.
47 **	Uses libdbm database if -DDBM.
48 **
49 **	Parameters:
50 **		a -- address to alias.
51 **		sendq -- a pointer to the head of the send queue
52 **			to put the aliases in.
53 **
54 **	Returns:
55 **		none
56 **
57 **	Side Effects:
58 **		Aliases found are expanded.
59 **
60 **	Notes:
61 **		If NoAlias (the "-n" flag) is set, no aliasing is
62 **			done.
63 **
64 **	Deficiencies:
65 **		It should complain about names that are aliased to
66 **			nothing.
67 */
68 
69 
70 /*
71 **  Sun YP servers read the dbm files directly, so we have to build them
72 **  even if NEWDB
73 */
74 
75 #ifdef NDBM
76 # ifndef NEWDB
77 #  define IF_MAKEDBMFILES
78 # else
79 #  ifdef YPCOMPAT
80 #   define IF_MAKEDBMFILES		if (makedbmfiles)
81 #  endif
82 # endif
83 #endif
84 
85 typedef union
86 {
87 #ifdef NDBM
88 	datum	dbm;
89 #endif
90 #ifdef NEWDB
91 	DBT	dbt;
92 #endif
93 	struct
94 	{
95 		char	*data;
96 		int	size;
97 	} xx;
98 } DBdatum;
99 
100 #ifdef NEWDB
101 static DB	*AliasDBptr;
102 #endif
103 #ifdef NDBM
104 static DBM	*AliasDBMptr;
105 #endif
106 
107 alias(a, sendq, e)
108 	register ADDRESS *a;
109 	ADDRESS **sendq;
110 	register ENVELOPE *e;
111 {
112 	register char *p;
113 	extern char *aliaslookup();
114 
115 	if (tTd(27, 1))
116 		printf("alias(%s)\n", a->q_paddr);
117 
118 	/* don't realias already aliased names */
119 	if (bitset(QDONTSEND, a->q_flags))
120 		return;
121 
122 	e->e_to = a->q_paddr;
123 
124 	/*
125 	**  Look up this name
126 	*/
127 
128 	if (NoAlias)
129 		p = NULL;
130 	else
131 		p = aliaslookup(a->q_user);
132 	if (p == NULL)
133 		return;
134 
135 	/*
136 	**  Match on Alias.
137 	**	Deliver to the target list.
138 	*/
139 
140 	if (tTd(27, 1))
141 		printf("%s (%s, %s) aliased to %s\n",
142 		    a->q_paddr, a->q_host, a->q_user, p);
143 	message(Arpa_Info, "aliased to %s", p);
144 	AliasLevel++;
145 	sendtolist(p, a, sendq, e);
146 	AliasLevel--;
147 }
148 /*
149 **  ALIASLOOKUP -- look up a name in the alias file.
150 **
151 **	Parameters:
152 **		name -- the name to look up.
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)
167 	char *name;
168 {
169 	int i;
170 	char keybuf[MAXNAME + 1];
171 # if defined(NEWDB) || defined(NDBM)
172 	DBdatum rhs, lhs;
173 	int s;
174 # else /* neither NEWDB nor NDBM */
175 	register STAB *s;
176 # endif
177 
178 	/* create a key for fetch */
179 	i = strlen(name) + 1;
180 	if (i > sizeof keybuf)
181 		i = sizeof keybuf;
182 	bcopy(name, keybuf, i);
183 	if (!bitnset(M_USR_UPPER, LocalMailer->m_flags))
184 		makelower(keybuf);
185 
186 # if defined(NEWDB) || defined(NDBM)
187 	lhs.xx.size = i;
188 	lhs.xx.data = keybuf;
189 # ifdef NEWDB
190 	if (AliasDBptr != NULL)
191 	{
192 		i = AliasDBptr->get(AliasDBptr, &lhs.dbt, &rhs.dbt, 0);
193 		if (i == 0)
194 			return (rhs.dbt.data);
195 	}
196 # ifdef NDBM
197 	else if (AliasDBMptr != NULL)
198 	{
199 		rhs.dbm = dbm_fetch(AliasDBMptr, lhs.dbm);
200 		return (rhs.dbm.dptr);
201 	}
202 # endif /* NDBM */
203 # else /* not NEWDB */
204 	rhs.dbm = dbm_fetch(AliasDBMptr, lhs.dbm);
205 	return (rhs.dbm.dptr);
206 # endif /* NEWDB */
207 # else /* neither NEWDB nor NDBM */
208 	s = stab(keybuf, ST_ALIAS, ST_FIND);
209 	if (s != NULL)
210 		return (s->s_alias);
211 # endif
212 	return (NULL);
213 }
214 /*
215 **  INITALIASES -- initialize for aliasing
216 **
217 **	Very different depending on whether we are running NDBM or not.
218 **
219 **	Parameters:
220 **		aliasfile -- location of aliases.
221 **		init -- if set and if NDBM, initialize the NDBM files.
222 **
223 **	Returns:
224 **		none.
225 **
226 **	Side Effects:
227 **		initializes aliases:
228 **		if NDBM:  opens the database.
229 **		if ~NDBM: reads the aliases into the symbol table.
230 */
231 
232 # define DBMMODE	0644
233 
234 initaliases(aliasfile, init, e)
235 	char *aliasfile;
236 	bool init;
237 	register ENVELOPE *e;
238 {
239 #if defined(NDBM) || defined(NEWDB)
240 	int atcnt;
241 	time_t modtime;
242 	bool automatic = FALSE;
243 	char buf[MAXNAME];
244 #endif
245 	struct stat stb;
246 	static bool initialized = FALSE;
247 	static int readaliases();
248 
249 	if (initialized)
250 		return;
251 	initialized = TRUE;
252 
253 	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
254 	{
255 		if (aliasfile != NULL && init)
256 			syserr("Cannot open %s", aliasfile);
257 		NoAlias = TRUE;
258 		errno = 0;
259 		return;
260 	}
261 
262 # if defined(NDBM) || defined(NEWDB)
263 	/*
264 	**  Check to see that the alias file is complete.
265 	**	If not, we will assume that someone died, and it is up
266 	**	to us to rebuild it.
267 	*/
268 
269 	if (!init)
270 	{
271 # ifdef NEWDB
272 		(void) strcpy(buf, aliasfile);
273 		(void) strcat(buf, ".db");
274 		AliasDBptr = dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL);
275 		if (AliasDBptr == NULL)
276 		{
277 # ifdef NDBM
278 			AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE);
279 			if (AliasDBMptr == NULL)
280 			{
281 				syserr("initaliases: cannot open %s", buf);
282 				NoAlias = TRUE;
283 				return;
284 			}
285 # else
286 			syserr("initaliases: cannot open %s", buf);
287 			NoAlias = TRUE;
288 			return;
289 # endif
290 		}
291 # else
292 		AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE);
293 		if (AliasDBMptr == NULL)
294 		{
295 			syserr("initaliases: cannot open %s", buf);
296 			NoAlias = TRUE;
297 			return;
298 		}
299 # endif
300 	}
301 	atcnt = SafeAlias * 2;
302 	if (atcnt > 0)
303 	{
304 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
305 		{
306 			/*
307 			**  Reinitialize alias file in case the new
308 			**  one is mv'ed in instead of cp'ed in.
309 			**
310 			**	Only works with new DBM -- old one will
311 			**	just consume file descriptors forever.
312 			**	If you have a dbmclose() it can be
313 			**	added before the sleep(30).
314 			*/
315 
316 # ifdef NEWDB
317 			if (AliasDBptr != NULL)
318 				AliasDBptr->close(AliasDBptr);
319 # endif
320 # ifdef NDBM
321 			if (AliasDBMptr != NULL)
322 				dbm_close(AliasDBMptr);
323 # endif
324 
325 			sleep(30);
326 # ifdef NEWDB
327 			(void) strcpy(buf, aliasfile);
328 			(void) strcat(buf, ".db");
329 			AliasDBptr =
330 			    dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL);
331 			if (AliasDBptr == NULL)
332 			{
333 # ifdef NDBM
334 				AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE);
335 # else
336 				syserr("initaliases: cannot open %s", buf);
337 				NoAlias = TRUE;
338 				return;
339 # endif
340 			}
341 # else
342 # ifdef NDBM
343 			AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE);
344 			if (AliasDBMptr == NULL)
345 			{
346 				syserr("initaliases: cannot open %s", buf);
347 				NoAlias = TRUE;
348 				return;
349 			}
350 # endif
351 # endif
352 		}
353 	}
354 	else
355 		atcnt = 1;
356 
357 	/*
358 	**  See if the NDBM version of the file is out of date with
359 	**  the text version.  If so, go into 'init' mode automatically.
360 	**	This only happens if our effective userid owns the DBM.
361 	**	Note the unpalatable hack to see if the stat succeeded.
362 	*/
363 
364 	modtime = stb.st_mtime;
365 	(void) strcpy(buf, aliasfile);
366 # ifdef NEWDB
367 	(void) strcat(buf, ".db");
368 # else
369 	(void) strcat(buf, ".pag");
370 # endif
371 	stb.st_ino = 0;
372 	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
373 	{
374 		errno = 0;
375 		if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
376 		{
377 			init = TRUE;
378 			automatic = TRUE;
379 			message(Arpa_Info, "rebuilding alias database");
380 #ifdef LOG
381 			if (LogLevel >= 7)
382 				syslog(LOG_INFO, "rebuilding alias database");
383 #endif /* LOG */
384 		}
385 		else
386 		{
387 #ifdef LOG
388 			if (LogLevel >= 7)
389 				syslog(LOG_INFO, "alias database out of date");
390 #endif /* LOG */
391 			message(Arpa_Info, "Warning: alias database out of date");
392 		}
393 	}
394 
395 
396 	/*
397 	**  If necessary, load the NDBM file.
398 	**	If running without NDBM, load the symbol table.
399 	*/
400 
401 	if (init)
402 	{
403 #ifdef LOG
404 		if (LogLevel >= 6)
405 		{
406 			extern char *username();
407 
408 			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
409 				automatic ? "auto" : "", username());
410 		}
411 #endif /* LOG */
412 		readaliases(aliasfile, TRUE, e);
413 	}
414 # else /* NDBM */
415 	readaliases(aliasfile, init, e);
416 # endif /* NDBM */
417 }
418 /*
419 **  READALIASES -- read and process the alias file.
420 **
421 **	This routine implements the part of initaliases that occurs
422 **	when we are not going to use the DBM stuff.
423 **
424 **	Parameters:
425 **		aliasfile -- the pathname of the alias file master.
426 **		init -- if set, initialize the NDBM stuff.
427 **
428 **	Returns:
429 **		none.
430 **
431 **	Side Effects:
432 **		Reads aliasfile into the symbol table.
433 **		Optionally, builds the .dir & .pag files.
434 */
435 
436 static
437 readaliases(aliasfile, init, e)
438 	char *aliasfile;
439 	bool init;
440 	register ENVELOPE *e;
441 {
442 	register char *p;
443 	char *rhs;
444 	bool skipping;
445 	int naliases, bytes, longest;
446 	FILE *af;
447 	bool makedbmfiles;
448 	void (*oldsigint)();
449 	ADDRESS al, bl;
450 	register STAB *s;
451 # ifdef NEWDB
452 	DB *dbp;
453 # endif
454 # ifdef NDBM
455 	DBM *dbmp;
456 # endif
457 # ifdef LOCKF
458 	struct flock fld;
459 # endif
460 	char line[BUFSIZ];
461 
462 	if ((af = fopen(aliasfile, "r+")) == NULL)
463 	{
464 		if (init)
465 			syserr("Can't open %s", aliasfile);
466 		else if (tTd(27, 1))
467 			printf("Can't open %s\n", aliasfile);
468 		errno = 0;
469 		NoAlias++;
470 		return;
471 	}
472 
473 # if defined(NDBM) || defined(NEWDB)
474 	/* see if someone else is rebuilding the alias file already */
475 # ifdef LOCKF
476 	fld.l_type = F_WRLCK;
477 	fld.l_whence = fld.l_start = fld.l_len = 0;
478 	if (fcntl(fileno(af), F_SETLK, &fld) < 0)
479 # else
480 	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
481 # endif
482 	{
483 		/* yes, they are -- wait until done and then return */
484 		message(Arpa_Info, "Alias file is already being rebuilt");
485 		if (OpMode != MD_INITALIAS)
486 		{
487 			/* wait for other rebuild to complete */
488 # ifdef LOCKF
489 			(void) fcntl(fileno(af), F_SETLKW, &fld);
490 # else
491 			(void) flock(fileno(af), LOCK_EX);
492 # endif
493 		}
494 		(void) fclose(af);
495 		errno = 0;
496 		return;
497 	}
498 # endif /* NDBM */
499 
500 	/*
501 	**  If initializing, create the new DBM files.
502 	*/
503 
504 	if (init)
505 	{
506 		oldsigint = signal(SIGINT, SIG_IGN);
507 # ifdef NEWDB
508 		(void) strcpy(line, aliasfile);
509 		(void) strcat(line, ".db");
510 		dbp = dbopen(line,
511 		    O_RDWR|O_CREAT|O_TRUNC, DBMMODE, DB_HASH, NULL);
512 		if (dbp == NULL)
513 		{
514 			syserr("readaliases: cannot create %s", line);
515 			(void) signal(SIGINT, oldsigint);
516 			return;
517 		}
518 # endif
519 # ifdef IF_MAKEDBMFILES
520 # ifdef NEWDB
521 		makedbmfiles = access("/var/yp/Makefile", R_OK) == 0;
522 # endif
523 		IF_MAKEDBMFILES
524 		{
525 			dbmp = dbm_open(aliasfile,
526 					       O_RDWR|O_CREAT|O_TRUNC, DBMMODE);
527 			if (dbmp == NULL)
528 			{
529 				syserr("readaliases: cannot create %s.{dir,pag}",
530 					aliasfile);
531 				(void) signal(SIGINT, oldsigint);
532 				return;
533 			}
534 		}
535 # endif
536 	}
537 
538 	/*
539 	**  Read and interpret lines
540 	*/
541 
542 	FileName = aliasfile;
543 	LineNumber = 0;
544 	naliases = bytes = longest = 0;
545 	skipping = FALSE;
546 	while (fgets(line, sizeof (line), af) != NULL)
547 	{
548 		int lhssize, rhssize;
549 
550 		LineNumber++;
551 		p = strchr(line, '\n');
552 		if (p != NULL)
553 			*p = '\0';
554 		switch (line[0])
555 		{
556 		  case '#':
557 		  case '\0':
558 			skipping = FALSE;
559 			continue;
560 
561 		  case ' ':
562 		  case '\t':
563 			if (!skipping)
564 				syserr("Non-continuation line starts with space");
565 			skipping = TRUE;
566 			continue;
567 		}
568 		skipping = FALSE;
569 
570 		/*
571 		**  Process the LHS
572 		**	Find the final colon, and parse the address.
573 		**	It should resolve to a local name -- this will
574 		**	be checked later (we want to optionally do
575 		**	parsing of the RHS first to maximize error
576 		**	detection).
577 		*/
578 
579 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
580 			continue;
581 		if (*p++ != ':')
582 		{
583 			syserr("missing colon");
584 			continue;
585 		}
586 		if (parseaddr(line, &al, 1, ':', e) == NULL)
587 		{
588 			syserr("illegal alias name");
589 			continue;
590 		}
591 		loweraddr(&al);
592 
593 		/*
594 		**  Process the RHS.
595 		**	'al' is the internal form of the LHS address.
596 		**	'p' points to the text of the RHS.
597 		*/
598 
599 		rhs = p;
600 		for (;;)
601 		{
602 			register char c;
603 
604 			if (init && CheckAliases)
605 			{
606 				/* do parsing & compression of addresses */
607 				while (*p != '\0')
608 				{
609 					extern char *DelimChar;
610 
611 					while (isspace(*p) || *p == ',')
612 						p++;
613 					if (*p == '\0')
614 						break;
615 					if (parseaddr(p, &bl, -1, ',', e) == NULL)
616 						usrerr("%s... bad address", p);
617 					p = DelimChar;
618 				}
619 			}
620 			else
621 			{
622 				p = &p[strlen(p)];
623 				if (p[-1] == '\n')
624 					*--p = '\0';
625 			}
626 
627 			/* see if there should be a continuation line */
628 			c = fgetc(af);
629 			if (!feof(af))
630 				(void) ungetc(c, af);
631 			if (c != ' ' && c != '\t')
632 				break;
633 
634 			/* read continuation line */
635 			if (fgets(p, sizeof line - (p - line), af) == NULL)
636 				break;
637 			LineNumber++;
638 
639 			/* check for line overflow */
640 			if (strchr(p, '\n') == NULL)
641 			{
642 				usrerr("alias too long");
643 				break;
644 			}
645 		}
646 		if (al.q_mailer != LocalMailer)
647 		{
648 			syserr("cannot alias non-local names");
649 			continue;
650 		}
651 
652 		/*
653 		**  Insert alias into symbol table or DBM file
654 		*/
655 
656 		lhssize = strlen(al.q_user) + 1;
657 		if (!bitnset(M_USR_UPPER, al.q_mailer->m_flags))
658 			makelower(al.q_user);
659 		rhssize = strlen(rhs) + 1;
660 
661 # if defined(NDBM) || defined(NEWDB)
662 		if (init)
663 		{
664 			DBdatum key, content;
665 			int putstat;
666 
667 			key.xx.size = lhssize;
668 			key.xx.data = al.q_user;
669 			content.xx.size = rhssize;
670 			content.xx.data = rhs;
671 # ifdef NEWDB
672 			putstat = dbp->put(dbp, &key.dbt, &content.dbt,
673 					   R_NOOVERWRITE);
674 			if (putstat > 0)
675 			{
676 				usrerr("Warning: duplicate alias name %s",
677 					al.q_user);
678 				putstat = dbp->put(dbp, &key.dbt,
679 						   &content.dbt, 0);
680 			}
681 			if (putstat != 0)
682 				syserr("readaliases: db put (%s)", al.q_user);
683 # endif
684 # ifdef IF_MAKEDBMFILES
685 			IF_MAKEDBMFILES
686 			{
687 				putstat = dbm_store(dbmp, key.dbm, content.dbm,
688 						    DBM_INSERT);
689 				if (putstat > 0)
690 				{
691 					usrerr("Warning: duplicate alias name %s",
692 						al.q_user);
693 					putstat = dbm_store(dbmp, key.dbm,
694 							content.dbm, DBM_REPLACE);
695 				}
696 				if (putstat != 0)
697 					syserr("readaliases: dbm store (%s)",
698 						al.q_user);
699 			}
700 # endif
701 			if (al.q_paddr != NULL)
702 				free(al.q_paddr);
703 			if (al.q_host != NULL)
704 				free(al.q_host);
705 			if (al.q_user != NULL)
706 				free(al.q_user);
707 		}
708 		else
709 # endif /* NDBM */
710 		{
711 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
712 			s->s_alias = newstr(rhs);
713 		}
714 
715 		/* statistics */
716 		naliases++;
717 		bytes += lhssize + rhssize;
718 		if (rhssize > longest)
719 			longest = rhssize;
720 	}
721 
722 # if defined(NDBM) || defined(NEWDB)
723 	if (init)
724 	{
725 		/* add the distinquished alias "@" */
726 		DBdatum key;
727 
728 		key.xx.size = 2;
729 		key.xx.data = "@";
730 # ifdef NEWDB
731 		if (dbp->sync(dbp) != 0 ||
732 		    dbp->put(dbp, &key.dbt, &key.dbt, 0) != 0 ||
733 		    dbp->close(dbp) != 0)
734 			syserr("readaliases: db close failure");
735 # endif
736 # ifdef IF_MAKEDBMFILES
737 		IF_MAKEDBMFILES
738 		{
739 			if (dbm_store(dbmp, key.dbm, key.dbm, DBM_REPLACE) != 0 ||
740 			    dbm_error(dbmp))
741 				syserr("readaliases: dbm close failure");
742 			dbm_close(dbmp);
743 		}
744 # endif
745 
746 		/* restore the old signal */
747 		(void) signal(SIGINT, oldsigint);
748 	}
749 # endif /* NDBM */
750 
751 	/* closing the alias file drops the lock */
752 	(void) fclose(af);
753 	e->e_to = NULL;
754 	FileName = NULL;
755 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
756 			naliases, longest, bytes);
757 # ifdef LOG
758 	if (LogLevel >= 8)
759 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
760 			naliases, longest, bytes);
761 # endif /* LOG */
762 }
763 /*
764 **  FORWARD -- Try to forward mail
765 **
766 **	This is similar but not identical to aliasing.
767 **
768 **	Parameters:
769 **		user -- the name of the user who's mail we would like
770 **			to forward to.  It must have been verified --
771 **			i.e., the q_home field must have been filled
772 **			in.
773 **		sendq -- a pointer to the head of the send queue to
774 **			put this user's aliases in.
775 **
776 **	Returns:
777 **		none.
778 **
779 **	Side Effects:
780 **		New names are added to send queues.
781 */
782 
783 forward(user, sendq, e)
784 	ADDRESS *user;
785 	ADDRESS **sendq;
786 	register ENVELOPE *e;
787 {
788 	char *pp;
789 	char *ep;
790 	extern bool safefile();
791 
792 	if (tTd(27, 1))
793 		printf("forward(%s)\n", user->q_paddr);
794 
795 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
796 		return;
797 	if (user->q_home == NULL)
798 		syserr("forward: no home");
799 
800 	/* good address -- look for .forward file in home */
801 	define('z', user->q_home, e);
802 	define('u', user->q_user, e);
803 	define('h', user->q_host, e);
804 	if (ForwardPath == NULL)
805 		ForwardPath = newstr("\001z/.forward");
806 
807 	for (pp = ForwardPath; pp != NULL; pp = ep)
808 	{
809 		char buf[MAXPATHLEN+1];
810 
811 		ep = strchr(pp, ':');
812 		if (ep != NULL)
813 			*ep = '\0';
814 		expand(pp, buf, &buf[sizeof buf - 1], e);
815 		if (ep != NULL)
816 			*ep++ = ':';
817 		if (tTd(27, 3))
818 			printf("forward: trying %s\n", buf);
819 		if (include(buf, TRUE, user, sendq, e) == 0)
820 			break;
821 	}
822 }
823