xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 58670)
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.24 (Berkeley) 03/14/93 (with NEWDB and NDBM)";
32 #else
33 static char sccsid[] = "@(#)alias.c	6.24 (Berkeley) 03/14/93 (with NEWDB)";
34 #endif
35 #else
36 #ifdef NDBM
37 static char sccsid[] = "@(#)alias.c	6.24 (Berkeley) 03/14/93 (with NDBM)";
38 #else
39 static char sccsid[] = "@(#)alias.c	6.24 (Berkeley) 03/14/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 
638 		/*
639 		**  Process the RHS.
640 		**	'al' is the internal form of the LHS address.
641 		**	'p' points to the text of the RHS.
642 		*/
643 
644 		rhs = p;
645 		for (;;)
646 		{
647 			register char c;
648 			register char *nlp;
649 
650 			nlp = &p[strlen(p)];
651 			if (nlp[-1] == '\n')
652 				*--nlp = '\0';
653 
654 			if (init && CheckAliases)
655 			{
656 				/* do parsing & compression of addresses */
657 				while (*p != '\0')
658 				{
659 					auto char *delimptr;
660 
661 					while ((isascii(*p) && isspace(*p)) ||
662 								*p == ',')
663 						p++;
664 					if (*p == '\0')
665 						break;
666 					if (parseaddr(p, &bl, -1, ',', &delimptr, e) == NULL)
667 						usrerr("553 %s... bad address", p);
668 					p = delimptr;
669 				}
670 			}
671 			else
672 			{
673 				p = nlp;
674 			}
675 
676 			/* see if there should be a continuation line */
677 			c = fgetc(af);
678 			if (!feof(af))
679 				(void) ungetc(c, af);
680 			if (c != ' ' && c != '\t')
681 				break;
682 
683 			/* read continuation line */
684 			if (fgets(p, sizeof line - (p - line), af) == NULL)
685 				break;
686 			LineNumber++;
687 
688 			/* check for line overflow */
689 			if (strchr(p, '\n') == NULL)
690 			{
691 				usrerr("554 alias too long");
692 				break;
693 			}
694 		}
695 		if (al.q_mailer != LocalMailer)
696 		{
697 			syserr("554 cannot alias non-local names");
698 			continue;
699 		}
700 
701 		/*
702 		**  Insert alias into symbol table or DBM file
703 		*/
704 
705 		lhssize = strlen(al.q_user) + 1;
706 		if (!bitnset(M_USR_UPPER, al.q_mailer->m_flags))
707 			makelower(al.q_user);
708 		rhssize = strlen(rhs) + 1;
709 
710 # if defined(NDBM) || defined(NEWDB)
711 		if (init)
712 		{
713 			DBdatum key, content;
714 			int putstat;
715 
716 			key.xx.size = lhssize;
717 			key.xx.data = al.q_user;
718 			content.xx.size = rhssize;
719 			content.xx.data = rhs;
720 # ifdef NEWDB
721 			putstat = dbp->put(dbp, &key.dbt, &content.dbt,
722 					   R_NOOVERWRITE);
723 			if (putstat > 0)
724 			{
725 				usrerr("050 Warning: duplicate alias name %s",
726 					al.q_user);
727 				putstat = dbp->put(dbp, &key.dbt,
728 						   &content.dbt, 0);
729 			}
730 			if (putstat != 0)
731 				syserr("readaliases: db put (%s)", al.q_user);
732 # endif
733 # ifdef IF_MAKEDBMFILES
734 			IF_MAKEDBMFILES
735 			{
736 				putstat = dbm_store(dbmp, key.dbm, content.dbm,
737 						    DBM_INSERT);
738 				if (putstat > 0)
739 				{
740 					usrerr("050 Warning: duplicate alias name %s",
741 						al.q_user);
742 					putstat = dbm_store(dbmp, key.dbm,
743 							content.dbm, DBM_REPLACE);
744 				}
745 				if (putstat != 0)
746 					syserr("readaliases: dbm store (%s)",
747 						al.q_user);
748 			}
749 # endif
750 			if (al.q_paddr != NULL)
751 				free(al.q_paddr);
752 			if (al.q_host != NULL)
753 				free(al.q_host);
754 			if (al.q_user != NULL)
755 				free(al.q_user);
756 		}
757 		else
758 # endif /* NDBM */
759 		{
760 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
761 			s->s_alias = newstr(rhs);
762 		}
763 
764 		/* statistics */
765 		naliases++;
766 		bytes += lhssize + rhssize;
767 		if (rhssize > longest)
768 			longest = rhssize;
769 	}
770 
771 # if defined(NDBM) || defined(NEWDB)
772 	if (init)
773 	{
774 		/* add the distinquished alias "@" */
775 		DBdatum key;
776 
777 		key.xx.size = 2;
778 		key.xx.data = "@";
779 # ifdef NEWDB
780 		if (dbp->sync(dbp) != 0 ||
781 		    dbp->put(dbp, &key.dbt, &key.dbt, 0) != 0 ||
782 		    dbp->close(dbp) != 0)
783 			syserr("readaliases: db close failure");
784 # endif
785 # ifdef IF_MAKEDBMFILES
786 		IF_MAKEDBMFILES
787 		{
788 #ifdef YPCOMPAT
789 			nis_magic(dbmp);
790 #endif
791 			if (dbm_store(dbmp, key.dbm, key.dbm, DBM_REPLACE) != 0 ||
792 			    dbm_error(dbmp))
793 				syserr("readaliases: dbm close failure");
794 			dbm_close(dbmp);
795 		}
796 # endif
797 
798 		/* restore the old signal */
799 		(void) signal(SIGINT, oldsigint);
800 	}
801 # endif /* NDBM */
802 
803 	/* closing the alias file drops the lock */
804 	(void) fclose(af);
805 	e->e_to = NULL;
806 	FileName = NULL;
807 	message("%d aliases, longest %d bytes, %d bytes total",
808 			naliases, longest, bytes);
809 # ifdef LOG
810 	if (LogLevel > 7)
811 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
812 			naliases, longest, bytes);
813 # endif /* LOG */
814 }
815 /*
816 **  NIS_MAGIC -- Add NIS magic dbm data
817 **
818 **	This adds the magic entries needed by SunOS to make this a valid
819 **	NIS map.
820 **
821 **	Parameters:
822 **		dbmp -- a pointer to the DBM structure.
823 **
824 **	Returns:
825 **		none.
826 */
827 
828 # ifdef YPCOMPAT
829 
830 static void
831 nis_magic(dbmp)
832 	DBM *dbmp;
833 {
834 	int i;
835 	static datum key[2] =
836 	{
837 		{ "YP_LAST_MODIFIED",	sizeof "YP_LAST_MODIFIED" - 1 },
838 		{ "YP_MASTER_NAME",	sizeof "YP_MASTER_NAME" - 1 },
839 	};
840 	datum contents[2];
841 	char tbuf[12];
842 	char hbuf[MAXHOSTNAMELEN];
843 
844 	(void) sprintf(tbuf, "%010ld", curtime());
845 	contents[0].dptr = tbuf;
846 	contents[0].dsize = strlen(tbuf);
847 
848 	(void) myhostname(hbuf, sizeof hbuf);
849 	contents[1].dptr = hbuf;
850 	contents[1].dptr = strlen(hbuf);
851 
852 	for (i = 0; i < sizeof key / sizeof *key; i++)
853 	{
854 		if (dbm_store(dbmp, key[i], contents[i], DBM_REPLACE) != 0 ||
855 		    dbm_error(dbmp))
856 			syserr("nis_magic: dbm_store failure");
857 	}
858 }
859 
860 # endif
861 /*
862 **  FORWARD -- Try to forward mail
863 **
864 **	This is similar but not identical to aliasing.
865 **
866 **	Parameters:
867 **		user -- the name of the user who's mail we would like
868 **			to forward to.  It must have been verified --
869 **			i.e., the q_home field must have been filled
870 **			in.
871 **		sendq -- a pointer to the head of the send queue to
872 **			put this user's aliases in.
873 **
874 **	Returns:
875 **		none.
876 **
877 **	Side Effects:
878 **		New names are added to send queues.
879 */
880 
881 forward(user, sendq, e)
882 	ADDRESS *user;
883 	ADDRESS **sendq;
884 	register ENVELOPE *e;
885 {
886 	char *pp;
887 	char *ep;
888 
889 	if (tTd(27, 1))
890 		printf("forward(%s)\n", user->q_paddr);
891 
892 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
893 		return;
894 	if (user->q_home == NULL)
895 	{
896 		syserr("554 forward: no home");
897 		user->q_home = "/nosuchdirectory";
898 	}
899 
900 	/* good address -- look for .forward file in home */
901 	define('z', user->q_home, e);
902 	define('u', user->q_user, e);
903 	define('h', user->q_host, e);
904 	if (ForwardPath == NULL)
905 		ForwardPath = newstr("\201z/.forward");
906 
907 	for (pp = ForwardPath; pp != NULL; pp = ep)
908 	{
909 		int err;
910 		char buf[MAXPATHLEN+1];
911 
912 		ep = strchr(pp, ':');
913 		if (ep != NULL)
914 			*ep = '\0';
915 		expand(pp, buf, &buf[sizeof buf - 1], e);
916 		if (ep != NULL)
917 			*ep++ = ':';
918 		if (tTd(27, 3))
919 			printf("forward: trying %s\n", buf);
920 		err = include(buf, TRUE, user, sendq, e);
921 		if (err == 0)
922 			break;
923 		if (transienterror(err))
924 		{
925 			/* we have to suspend this message */
926 			user->q_flags |= QQUEUEUP|QDONTSEND;
927 			return;
928 		}
929 	}
930 }
931