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