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