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