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