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