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