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