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