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