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