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