xref: /freebsd/contrib/sendmail/mail.local/mail.local.c (revision 4f29da19bd44f0e99f021510460a81bf754c21d2)
1 /*
2  * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1990, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  * $FreeBSD$
12  *
13  */
14 
15 #include <sm/gen.h>
16 
17 SM_IDSTR(copyright,
18 "@(#) Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.\n\
19 	All rights reserved.\n\
20      Copyright (c) 1990, 1993, 1994\n\
21 	The Regents of the University of California.  All rights reserved.\n")
22 
23 SM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.253 2004/11/01 20:42:42 ca Exp $")
24 
25 #include <stdlib.h>
26 #include <sm/errstring.h>
27 #include <sm/io.h>
28 #include <sm/limits.h>
29 # include <unistd.h>
30 # ifdef EX_OK
31 #  undef EX_OK		/* unistd.h may have another use for this */
32 # endif /* EX_OK */
33 # define LOCKFILE_PMODE 0
34 #include <sm/mbdb.h>
35 #include <sm/sysexits.h>
36 
37 #ifndef HASHSPOOL
38 # define HASHSPOOL	0
39 #endif /* ! HASHSPOOL */
40 #ifndef HASHSPOOLMD5
41 # define HASHSPOOLMD5	0
42 #endif /* ! HASHSPOOLMD5 */
43 
44 /*
45 **  This is not intended to work on System V derived systems
46 **  such as Solaris or HP-UX, since they use a totally different
47 **  approach to mailboxes (essentially, they have a set-group-ID program
48 **  rather than set-user-ID, and they rely on the ability to "give away"
49 **  files to do their work).  IT IS NOT A BUG that this doesn't
50 **  work on such architectures.
51 */
52 
53 
54 #include <stdio.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <time.h>
60 #include <stdlib.h>
61 # include <sys/socket.h>
62 # include <sys/file.h>
63 # include <netinet/in.h>
64 # include <arpa/nameser.h>
65 # include <netdb.h>
66 # include <pwd.h>
67 
68 #include <sm/string.h>
69 #include <syslog.h>
70 #include <ctype.h>
71 
72 #include <sm/conf.h>
73 #include <sendmail/pathnames.h>
74 
75 #if HASHSPOOL
76 # define HASH_NONE	0
77 # define HASH_USER	1
78 # if HASHSPOOLMD5
79 #  define HASH_MD5	2
80 #  include <openssl/md5.h>
81 # endif /* HASHSPOOLMD5 */
82 #endif /* HASHSPOOL */
83 
84 
85 #ifndef LOCKTO_RM
86 # define LOCKTO_RM	300	/* timeout for stale lockfile removal */
87 #endif /* ! LOCKTO_RM */
88 #ifndef LOCKTO_GLOB
89 # define LOCKTO_GLOB	400	/* global timeout for lockfile creation */
90 #endif /* ! LOCKTO_GLOB */
91 
92 /* define a realloc() which works for NULL pointers */
93 #define REALLOC(ptr, size)	(((ptr) == NULL) ? malloc(size) : realloc(ptr, size))
94 
95 /*
96 **  If you don't have flock, you could try using lockf instead.
97 */
98 
99 #ifdef LDA_USE_LOCKF
100 # define flock(a, b)	lockf(a, b, 0)
101 # ifdef LOCK_EX
102 #  undef LOCK_EX
103 # endif /* LOCK_EX */
104 # define LOCK_EX	F_LOCK
105 #endif /* LDA_USE_LOCKF */
106 
107 #ifndef LOCK_EX
108 # include <sys/file.h>
109 #endif /* ! LOCK_EX */
110 
111 /*
112 **  If you don't have setreuid, and you have saved uids, and you have
113 **  a seteuid() call that doesn't try to emulate using setuid(), then
114 **  you can try defining LDA_USE_SETEUID.
115 */
116 
117 #ifdef LDA_USE_SETEUID
118 # define setreuid(r, e)		seteuid(e)
119 #endif /* LDA_USE_SETEUID */
120 
121 #ifdef LDA_CONTENTLENGTH
122 # define CONTENTLENGTH	1
123 #endif /* LDA_CONTENTLENGTH */
124 
125 #ifndef INADDRSZ
126 # define INADDRSZ	4		/* size of an IPv4 address in bytes */
127 #endif /* ! INADDRSZ */
128 
129 #ifdef MAILLOCK
130 # include <maillock.h>
131 #endif /* MAILLOCK */
132 
133 #ifndef MAILER_DAEMON
134 # define MAILER_DAEMON	"MAILER-DAEMON"
135 #endif /* ! MAILER_DAEMON */
136 
137 #ifdef CONTENTLENGTH
138 char	ContentHdr[40] = "Content-Length: ";
139 off_t	HeaderLength;
140 off_t	BodyLength;
141 #endif /* CONTENTLENGTH */
142 
143 bool	EightBitMime = true;		/* advertise 8BITMIME in LMTP */
144 char	ErrBuf[10240];			/* error buffer */
145 int	ExitVal = EX_OK;		/* sysexits.h error value. */
146 bool	nobiff = false;
147 bool	nofsync = false;
148 bool	HoldErrs = false;		/* Hold errors in ErrBuf */
149 bool	LMTPMode = false;
150 bool	BounceQuota = false;		/* permanent error when over quota */
151 bool	CloseMBDB = false;
152 char	*HomeMailFile = NULL;		/* store mail in homedir */
153 
154 #if HASHSPOOL
155 int	HashType = HASH_NONE;
156 int	HashDepth = 0;
157 bool	StripRcptDomain = true;
158 #else /* HASHSPOOL */
159 # define StripRcptDomain true
160 #endif /* HASHSPOOL */
161 char	SpoolPath[MAXPATHLEN];
162 
163 char	*parseaddr __P((char *, bool));
164 char	*process_recipient __P((char *));
165 void	dolmtp __P((void));
166 void	deliver __P((int, char *));
167 int	e_to_sys __P((int));
168 void	notifybiff __P((char *));
169 int	store __P((char *, bool *));
170 void	usage __P((void));
171 int	lockmbox __P((char *));
172 void	unlockmbox __P((void));
173 void	mailerr __P((const char *, const char *, ...));
174 void	flush_error __P((void));
175 #if HASHSPOOL
176 const char	*hashname __P((char *));
177 #endif /* HASHSPOOL */
178 
179 
180 static void
181 sm_exit(status)
182 	int status;
183 {
184 	if (CloseMBDB)
185 	{
186 		sm_mbdb_terminate();
187 		CloseMBDB = false;	/* not really necessary, but ... */
188 	}
189 	exit(status);
190 }
191 
192 int
193 main(argc, argv)
194 	int argc;
195 	char *argv[];
196 {
197 	struct passwd *pw;
198 	int ch, fd;
199 	uid_t uid;
200 	char *from;
201 	char *mbdbname = "pw";
202 	int err;
203 	extern char *optarg;
204 	extern int optind;
205 
206 
207 	/* make sure we have some open file descriptors */
208 	for (fd = 10; fd < 30; fd++)
209 		(void) close(fd);
210 
211 	/* use a reasonable umask */
212 	(void) umask(0077);
213 
214 # ifdef LOG_MAIL
215 	openlog("mail.local", 0, LOG_MAIL);
216 # else /* LOG_MAIL */
217 	openlog("mail.local", 0);
218 # endif /* LOG_MAIL */
219 
220 	from = NULL;
221 
222 	/* XXX can this be converted to a compile time check? */
223 	if (sm_strlcpy(SpoolPath, _PATH_MAILDIR, sizeof(SpoolPath)) >=
224 	    sizeof(SpoolPath))
225 	{
226 		mailerr("421", "Configuration error: _PATH_MAILDIR too large");
227 		sm_exit(EX_CONFIG);
228 	}
229 #if HASHSPOOL
230 	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lH:p:ns")) != -1)
231 #else /* HASHSPOOL */
232 	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:ls")) != -1)
233 #endif /* HASHSPOOL */
234 	{
235 		switch(ch)
236 		{
237 		  case '7':		/* Do not advertise 8BITMIME */
238 			EightBitMime = false;
239 			break;
240 
241 		  case 'B':
242 			nobiff = true;
243 			break;
244 
245 		  case 'b':		/* bounce mail when over quota. */
246 			BounceQuota = true;
247 			break;
248 
249 		  case 'd':		/* Backward compatible. */
250 			break;
251 
252 		  case 'D':		/* mailbox database type */
253 			mbdbname = optarg;
254 			break;
255 
256 		  case 'f':
257 		  case 'r':		/* Backward compatible. */
258 			if (from != NULL)
259 			{
260 				mailerr(NULL, "Multiple -f options");
261 				usage();
262 			}
263 			from = optarg;
264 			break;
265 
266 		  case 'h':
267 			if (optarg != NULL || *optarg != '\0')
268 				HomeMailFile = optarg;
269 			else
270 			{
271 				mailerr(NULL, "-h: missing filename");
272 				usage();
273 			}
274 			break;
275 
276 		  case 'l':
277 			LMTPMode = true;
278 			break;
279 
280 		  case 's':
281 			nofsync++;
282 			break;
283 
284 #if HASHSPOOL
285 		  case 'H':
286 			if (optarg == NULL || *optarg == '\0')
287 			{
288 				mailerr(NULL, "-H: missing hashinfo");
289 				usage();
290 			}
291 			switch(optarg[0])
292 			{
293 			  case 'u':
294 				HashType = HASH_USER;
295 				break;
296 
297 # if HASHSPOOLMD5
298 			  case 'm':
299 				HashType = HASH_MD5;
300 				break;
301 # endif /* HASHSPOOLMD5 */
302 
303 			  default:
304 				mailerr(NULL, "-H: unknown hash type");
305 				usage();
306 			}
307 			if (optarg[1] == '\0')
308 			{
309 				mailerr(NULL, "-H: invalid hash depth");
310 				usage();
311 			}
312 			HashDepth = atoi(&optarg[1]);
313 			if ((HashDepth <= 0) || ((HashDepth * 2) >= MAXPATHLEN))
314 			{
315 				mailerr(NULL, "-H: invalid hash depth");
316 				usage();
317 			}
318 			break;
319 
320 		  case 'p':
321 			if (optarg == NULL || *optarg == '\0')
322 			{
323 				mailerr(NULL, "-p: missing spool path");
324 				usage();
325 			}
326 			if (sm_strlcpy(SpoolPath, optarg, sizeof(SpoolPath)) >=
327 			    sizeof(SpoolPath))
328 			{
329 				mailerr(NULL, "-p: invalid spool path");
330 				usage();
331 			}
332 			break;
333 
334 		  case 'n':
335 			StripRcptDomain = false;
336 			break;
337 #endif /* HASHSPOOL */
338 
339 		  case '?':
340 		  default:
341 			usage();
342 		}
343 	}
344 	argc -= optind;
345 	argv += optind;
346 
347 	/* initialize biff structures */
348 	if (!nobiff)
349 		notifybiff(NULL);
350 
351 	err = sm_mbdb_initialize(mbdbname);
352 	if (err != EX_OK)
353 	{
354 		char *errcode = "521";
355 
356 		if (err == EX_TEMPFAIL)
357 			errcode = "421";
358 
359 		mailerr(errcode, "Can not open mailbox database %s: %s",
360 			mbdbname, sm_strexit(err));
361 		sm_exit(err);
362 	}
363 	CloseMBDB = true;
364 
365 	if (LMTPMode)
366 	{
367 		if (argc > 0)
368 		{
369 			mailerr("421", "Users should not be specified in command line if LMTP required");
370 			sm_exit(EX_TEMPFAIL);
371 		}
372 
373 		dolmtp();
374 		/* NOTREACHED */
375 		sm_exit(EX_OK);
376 	}
377 
378 	/* Non-LMTP from here on out */
379 	if (*argv == '\0')
380 		usage();
381 
382 	/*
383 	**  If from not specified, use the name from getlogin() if the
384 	**  uid matches, otherwise, use the name from the password file
385 	**  corresponding to the uid.
386 	*/
387 
388 	uid = getuid();
389 	if (from == NULL && ((from = getlogin()) == NULL ||
390 			     (pw = getpwnam(from)) == NULL ||
391 			     pw->pw_uid != uid))
392 		from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???";
393 
394 	/*
395 	**  There is no way to distinguish the error status of one delivery
396 	**  from the rest of the deliveries.  So, if we failed hard on one
397 	**  or more deliveries, but had no failures on any of the others, we
398 	**  return a hard failure.  If we failed temporarily on one or more
399 	**  deliveries, we return a temporary failure regardless of the other
400 	**  failures.  This results in the delivery being reattempted later
401 	**  at the expense of repeated failures and multiple deliveries.
402 	*/
403 
404 	HoldErrs = true;
405 	fd = store(from, NULL);
406 	HoldErrs = false;
407 	if (fd < 0)
408 	{
409 		flush_error();
410 		sm_exit(ExitVal);
411 	}
412 	for (; *argv != NULL; ++argv)
413 		deliver(fd, *argv);
414 	sm_exit(ExitVal);
415 	/* NOTREACHED */
416 	return ExitVal;
417 }
418 
419 char *
420 parseaddr(s, rcpt)
421 	char *s;
422 	bool rcpt;
423 {
424 	char *p;
425 	int l;
426 
427 	if (*s++ != '<')
428 		return NULL;
429 
430 	p = s;
431 
432 	/* at-domain-list */
433 	while (*p == '@')
434 	{
435 		p++;
436 		while (*p != ',' && *p != ':' && *p != '\0')
437 			p++;
438 		if (*p == '\0')
439 			return NULL;
440 
441 		/* Skip over , or : */
442 		p++;
443 	}
444 
445 	s = p;
446 
447 	/* local-part */
448 	while (*p != '\0' && *p != '@' && *p != '>')
449 	{
450 		if (*p == '\\')
451 		{
452 			if (*++p == '\0')
453 				return NULL;
454 		}
455 		else if (*p == '\"')
456 		{
457 			p++;
458 			while (*p != '\0' && *p != '\"')
459 			{
460 				if (*p == '\\')
461 				{
462 					if (*++p == '\0')
463 						return NULL;
464 				}
465 				p++;
466 			}
467 			if (*p == '\0' || *(p + 1) == '\0')
468 				return NULL;
469 		}
470 		/* +detail ? */
471 		if (*p == '+' && rcpt)
472 			*p = '\0';
473 		p++;
474 	}
475 
476 	/* @domain */
477 	if (*p == '@')
478 	{
479 		if (rcpt)
480 			*p++ = '\0';
481 		while (*p != '\0' && *p != '>')
482 			p++;
483 	}
484 
485 	if (*p != '>')
486 		return NULL;
487 	else
488 		*p = '\0';
489 	p++;
490 
491 	if (*p != '\0' && *p != ' ')
492 		return NULL;
493 
494 	if (*s == '\0')
495 		s = MAILER_DAEMON;
496 
497 	l = strlen(s) + 1;
498 	if (l < 0)
499 		return NULL;
500 	p = malloc(l);
501 	if (p == NULL)
502 	{
503 		mailerr("421 4.3.0", "Memory exhausted");
504 		sm_exit(EX_TEMPFAIL);
505 	}
506 
507 	(void) sm_strlcpy(p, s, l);
508 	return p;
509 }
510 
511 char *
512 process_recipient(addr)
513 	char *addr;
514 {
515 	SM_MBDB_T user;
516 
517 	switch (sm_mbdb_lookup(addr, &user))
518 	{
519 	  case EX_OK:
520 		return NULL;
521 
522 	  case EX_NOUSER:
523 		return "550 5.1.1 User unknown";
524 
525 	  case EX_TEMPFAIL:
526 		return "451 4.3.0 User database failure; retry later";
527 
528 	  default:
529 		return "550 5.3.0 User database failure";
530 	}
531 }
532 
533 #define RCPT_GROW	30
534 
535 void
536 dolmtp()
537 {
538 	char *return_path = NULL;
539 	char **rcpt_addr = NULL;
540 	int rcpt_num = 0;
541 	int rcpt_alloc = 0;
542 	bool gotlhlo = false;
543 	char *err;
544 	int msgfd;
545 	char *p;
546 	int i;
547 	char myhostname[1024];
548 	char buf[4096];
549 
550 	memset(myhostname, '\0', sizeof myhostname);
551 	(void) gethostname(myhostname, sizeof myhostname - 1);
552 	if (myhostname[0] == '\0')
553 		sm_strlcpy(myhostname, "localhost", sizeof myhostname);
554 
555 	printf("220 %s LMTP ready\r\n", myhostname);
556 	for (;;)
557 	{
558 		(void) fflush(stdout);
559 		if (fgets(buf, sizeof(buf) - 1, stdin) == NULL)
560 			sm_exit(EX_OK);
561 		p = buf + strlen(buf) - 1;
562 		if (p >= buf && *p == '\n')
563 			*p-- = '\0';
564 		if (p >= buf && *p == '\r')
565 			*p-- = '\0';
566 
567 		switch (buf[0])
568 		{
569 		  case 'd':
570 		  case 'D':
571 			if (sm_strcasecmp(buf, "data") == 0)
572 			{
573 				bool inbody = false;
574 
575 				if (rcpt_num == 0)
576 				{
577 					mailerr("503 5.5.1", "No recipients");
578 					continue;
579 				}
580 				HoldErrs = true;
581 				msgfd = store(return_path, &inbody);
582 				HoldErrs = false;
583 				if (msgfd < 0 && !inbody)
584 				{
585 					flush_error();
586 					continue;
587 				}
588 
589 				for (i = 0; i < rcpt_num; i++)
590 				{
591 					if (msgfd < 0)
592 					{
593 						/* print error for rcpt */
594 						flush_error();
595 						continue;
596 					}
597 					p = strchr(rcpt_addr[i], '+');
598 					if (p != NULL)
599 						*p = '\0';
600 					deliver(msgfd, rcpt_addr[i]);
601 				}
602 				if (msgfd >= 0)
603 					(void) close(msgfd);
604 				goto rset;
605 			}
606 			goto syntaxerr;
607 			/* NOTREACHED */
608 			break;
609 
610 		  case 'l':
611 		  case 'L':
612 			if (sm_strncasecmp(buf, "lhlo ", 5) == 0)
613 			{
614 				/* check for duplicate per RFC 1651 4.2 */
615 				if (gotlhlo)
616 				{
617 					mailerr("503", "%s Duplicate LHLO",
618 					       myhostname);
619 					continue;
620 				}
621 				gotlhlo = true;
622 				printf("250-%s\r\n", myhostname);
623 				if (EightBitMime)
624 					printf("250-8BITMIME\r\n");
625 				printf("250-ENHANCEDSTATUSCODES\r\n");
626 				printf("250 PIPELINING\r\n");
627 				continue;
628 			}
629 			goto syntaxerr;
630 			/* NOTREACHED */
631 			break;
632 
633 		  case 'm':
634 		  case 'M':
635 			if (sm_strncasecmp(buf, "mail ", 5) == 0)
636 			{
637 				if (return_path != NULL)
638 				{
639 					mailerr("503 5.5.1",
640 						"Nested MAIL command");
641 					continue;
642 				}
643 				if (sm_strncasecmp(buf + 5, "from:", 5) != 0 ||
644 				    ((return_path = parseaddr(buf + 10,
645 							      false)) == NULL))
646 				{
647 					mailerr("501 5.5.4",
648 						"Syntax error in parameters");
649 					continue;
650 				}
651 				printf("250 2.5.0 Ok\r\n");
652 				continue;
653 			}
654 			goto syntaxerr;
655 			/* NOTREACHED */
656 			break;
657 
658 		  case 'n':
659 		  case 'N':
660 			if (sm_strcasecmp(buf, "noop") == 0)
661 			{
662 				printf("250 2.0.0 Ok\r\n");
663 				continue;
664 			}
665 			goto syntaxerr;
666 			/* NOTREACHED */
667 			break;
668 
669 		  case 'q':
670 		  case 'Q':
671 			if (sm_strcasecmp(buf, "quit") == 0)
672 			{
673 				printf("221 2.0.0 Bye\r\n");
674 				sm_exit(EX_OK);
675 			}
676 			goto syntaxerr;
677 			/* NOTREACHED */
678 			break;
679 
680 		  case 'r':
681 		  case 'R':
682 			if (sm_strncasecmp(buf, "rcpt ", 5) == 0)
683 			{
684 				if (return_path == NULL)
685 				{
686 					mailerr("503 5.5.1",
687 						"Need MAIL command");
688 					continue;
689 				}
690 				if (rcpt_num >= rcpt_alloc)
691 				{
692 					rcpt_alloc += RCPT_GROW;
693 					rcpt_addr = (char **)
694 						REALLOC((char *) rcpt_addr,
695 							rcpt_alloc *
696 							sizeof(char **));
697 					if (rcpt_addr == NULL)
698 					{
699 						mailerr("421 4.3.0",
700 							"Memory exhausted");
701 						sm_exit(EX_TEMPFAIL);
702 					}
703 				}
704 				if (sm_strncasecmp(buf + 5, "to:", 3) != 0 ||
705 				    ((rcpt_addr[rcpt_num] = parseaddr(buf + 8,
706 								      StripRcptDomain)) == NULL))
707 				{
708 					mailerr("501 5.5.4",
709 						"Syntax error in parameters");
710 					continue;
711 				}
712 				err = process_recipient(rcpt_addr[rcpt_num]);
713 				if (err != NULL)
714 				{
715 					mailerr(NULL, "%s", err);
716 					continue;
717 				}
718 				rcpt_num++;
719 				printf("250 2.1.5 Ok\r\n");
720 				continue;
721 			}
722 			else if (sm_strcasecmp(buf, "rset") == 0)
723 			{
724 				printf("250 2.0.0 Ok\r\n");
725 
726 rset:
727 				while (rcpt_num > 0)
728 					free(rcpt_addr[--rcpt_num]);
729 				if (return_path != NULL)
730 					free(return_path);
731 				return_path = NULL;
732 				continue;
733 			}
734 			goto syntaxerr;
735 			/* NOTREACHED */
736 			break;
737 
738 		  case 'v':
739 		  case 'V':
740 			if (sm_strncasecmp(buf, "vrfy ", 5) == 0)
741 			{
742 				printf("252 2.3.3 Try RCPT to attempt delivery\r\n");
743 				continue;
744 			}
745 			goto syntaxerr;
746 			/* NOTREACHED */
747 			break;
748 
749 		  default:
750   syntaxerr:
751 			mailerr("500 5.5.2", "Syntax error");
752 			continue;
753 			/* NOTREACHED */
754 			break;
755 		}
756 	}
757 }
758 
759 int
760 store(from, inbody)
761 	char *from;
762 	bool *inbody;
763 {
764 	FILE *fp = NULL;
765 	time_t tval;
766 	bool eline;		/* previous line was empty */
767 	bool fullline = true;	/* current line is terminated */
768 	bool prevfl;		/* previous line was terminated */
769 	char line[2048];
770 	int fd;
771 	char tmpbuf[sizeof _PATH_LOCTMP + 1];
772 
773 	if (inbody != NULL)
774 		*inbody = false;
775 
776 	(void) umask(0077);
777 	(void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf);
778 	if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL)
779 	{
780 		if (fd >= 0)
781 			(void) close(fd);
782 		mailerr("451 4.3.0", "Unable to open temporary file");
783 		return -1;
784 	}
785 	(void) unlink(tmpbuf);
786 
787 	if (LMTPMode)
788 	{
789 		printf("354 Go ahead\r\n");
790 		(void) fflush(stdout);
791 	}
792 	if (inbody != NULL)
793 		*inbody = true;
794 
795 	(void) time(&tval);
796 	(void) fprintf(fp, "From %s %s", from, ctime(&tval));
797 
798 #ifdef CONTENTLENGTH
799 	HeaderLength = 0;
800 	BodyLength = -1;
801 #endif /* CONTENTLENGTH */
802 
803 	line[0] = '\0';
804 	eline = true;
805 	while (fgets(line, sizeof(line), stdin) != (char *) NULL)
806 	{
807 		size_t line_len = 0;
808 		int peek;
809 
810 		prevfl = fullline;	/* preserve state of previous line */
811 		while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
812 			line_len++;
813 		line_len++;
814 
815 		/* Check for dot-stuffing */
816 		if (prevfl && LMTPMode && line[0] == '.')
817 		{
818 			if (line[1] == '\n' ||
819 			    (line[1] == '\r' && line[2] == '\n'))
820 				goto lmtpdot;
821 			memcpy(line, line + 1, line_len);
822 			line_len--;
823 		}
824 
825 		/* Check to see if we have the full line from fgets() */
826 		fullline = false;
827 		if (line_len > 0)
828 		{
829 			if (line[line_len - 1] == '\n')
830 			{
831 				if (line_len >= 2 &&
832 				    line[line_len - 2] == '\r')
833 				{
834 					line[line_len - 2] = '\n';
835 					line[line_len - 1] = '\0';
836 					line_len--;
837 				}
838 				fullline = true;
839 			}
840 			else if (line[line_len - 1] == '\r')
841 			{
842 				/* Did we just miss the CRLF? */
843 				peek = fgetc(stdin);
844 				if (peek == '\n')
845 				{
846 					line[line_len - 1] = '\n';
847 					fullline = true;
848 				}
849 				else
850 					(void) ungetc(peek, stdin);
851 			}
852 		}
853 		else
854 			fullline = true;
855 
856 #ifdef CONTENTLENGTH
857 		if (prevfl && line[0] == '\n' && HeaderLength == 0)
858 		{
859 			eline = false;
860 			if (fp != NULL)
861 				HeaderLength = ftell(fp);
862 			if (HeaderLength <= 0)
863 			{
864 				/*
865 				**  shouldn't happen, unless ftell() is
866 				**  badly broken
867 				*/
868 
869 				HeaderLength = -1;
870 			}
871 		}
872 #else /* CONTENTLENGTH */
873 		if (prevfl && line[0] == '\n')
874 			eline = true;
875 #endif /* CONTENTLENGTH */
876 		else
877 		{
878 			if (eline && line[0] == 'F' &&
879 			    fp != NULL &&
880 			    !memcmp(line, "From ", 5))
881 				(void) putc('>', fp);
882 			eline = false;
883 #ifdef CONTENTLENGTH
884 			/* discard existing "Content-Length:" headers */
885 			if (prevfl && HeaderLength == 0 &&
886 			    (line[0] == 'C' || line[0] == 'c') &&
887 			    sm_strncasecmp(line, ContentHdr, 15) == 0)
888 			{
889 				/*
890 				**  be paranoid: clear the line
891 				**  so no "wrong matches" may occur later
892 				*/
893 				line[0] = '\0';
894 				continue;
895 			}
896 #endif /* CONTENTLENGTH */
897 
898 		}
899 		if (fp != NULL)
900 		{
901 			(void) fwrite(line, sizeof(char), line_len, fp);
902 			if (ferror(fp))
903 			{
904 				mailerr("451 4.3.0",
905 					"Temporary file write error");
906 				(void) fclose(fp);
907 				fp = NULL;
908 				continue;
909 			}
910 		}
911 	}
912 
913 	/* check if an error occurred */
914 	if (fp == NULL)
915 		return -1;
916 
917 	if (LMTPMode)
918 	{
919 		/* Got a premature EOF -- toss message and exit */
920 		sm_exit(EX_OK);
921 	}
922 
923 	/* If message not newline terminated, need an extra. */
924 	if (fp != NULL && strchr(line, '\n') == NULL)
925 		(void) putc('\n', fp);
926 
927   lmtpdot:
928 
929 #ifdef CONTENTLENGTH
930 	if (fp != NULL)
931 		BodyLength = ftell(fp);
932 	if (HeaderLength == 0 && BodyLength > 0)	/* empty body */
933 	{
934 		HeaderLength = BodyLength;
935 		BodyLength = 0;
936 	}
937 	else
938 		BodyLength = BodyLength - HeaderLength - 1 ;
939 
940 	if (HeaderLength > 0 && BodyLength >= 0)
941 	{
942 		(void) sm_snprintf(line, sizeof line, "%lld\n",
943 				   (LONGLONG_T) BodyLength);
944 		(void) sm_strlcpy(&ContentHdr[16], line,
945 				  sizeof(ContentHdr) - 16);
946 	}
947 	else
948 		BodyLength = -1;	/* Something is wrong here */
949 #endif /* CONTENTLENGTH */
950 
951 	/* Output a newline; note, empty messages are allowed. */
952 	if (fp != NULL)
953 		(void) putc('\n', fp);
954 
955 	if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0)
956 	{
957 		mailerr("451 4.3.0", "Temporary file write error");
958 		if (fp != NULL)
959 			(void) fclose(fp);
960 		return -1;
961 	}
962 	return fd;
963 }
964 
965 void
966 deliver(fd, name)
967 	int fd;
968 	char *name;
969 {
970 	struct stat fsb;
971 	struct stat sb;
972 	char path[MAXPATHLEN];
973 	int mbfd = -1, nr = 0, nw, off;
974 	int exitval;
975 	char *p;
976 	char *errcode;
977 	off_t curoff, cursize;
978 #ifdef CONTENTLENGTH
979 	off_t headerbytes;
980 	int readamount;
981 #endif /* CONTENTLENGTH */
982 	char biffmsg[100], buf[8 * 1024];
983 	SM_MBDB_T user;
984 
985 	/*
986 	**  Disallow delivery to unknown names -- special mailboxes can be
987 	**  handled in the sendmail aliases file.
988 	*/
989 
990 	exitval = sm_mbdb_lookup(name, &user);
991 	switch (exitval)
992 	{
993 	  case EX_OK:
994 		break;
995 
996 	  case EX_NOUSER:
997 		exitval = EX_UNAVAILABLE;
998 		mailerr("550 5.1.1", "%s: User unknown", name);
999 		break;
1000 
1001 	  case EX_TEMPFAIL:
1002 		mailerr("451 4.3.0", "%s: User database failure; retry later",
1003 			name);
1004 		break;
1005 
1006 	  default:
1007 		exitval = EX_UNAVAILABLE;
1008 		mailerr("550 5.3.0", "%s: User database failure", name);
1009 		break;
1010 	}
1011 
1012 	if (exitval != EX_OK)
1013 	{
1014 		if (ExitVal != EX_TEMPFAIL)
1015 			ExitVal = exitval;
1016 		return;
1017 	}
1018 
1019 	endpwent();
1020 
1021 	/*
1022 	**  Keep name reasonably short to avoid buffer overruns.
1023 	**	This isn't necessary on BSD because of the proper
1024 	**	definition of snprintf(), but it can cause problems
1025 	**	on other systems.
1026 	**  Also, clear out any bogus characters.
1027 	*/
1028 
1029 #if !HASHSPOOL
1030 	if (strlen(name) > 40)
1031 		name[40] = '\0';
1032 	for (p = name; *p != '\0'; p++)
1033 	{
1034 		if (!isascii(*p))
1035 			*p &= 0x7f;
1036 		else if (!isprint(*p))
1037 			*p = '.';
1038 	}
1039 #endif /* !HASHSPOOL */
1040 
1041 
1042 	if (HomeMailFile == NULL)
1043 	{
1044 		if (sm_strlcpyn(path, sizeof(path),
1045 #if HASHSPOOL
1046 				4,
1047 #else /* HASHSPOOL */
1048 				3,
1049 #endif /* HASHSPOOL */
1050 				SpoolPath, "/",
1051 #if HASHSPOOL
1052 				hashname(name),
1053 #endif /* HASHSPOOL */
1054 				name) >= sizeof(path))
1055 		{
1056 			exitval = EX_UNAVAILABLE;
1057 			mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
1058 			return;
1059 		}
1060 	}
1061 	else if (*user.mbdb_homedir == '\0')
1062 	{
1063 		exitval = EX_UNAVAILABLE;
1064 		mailerr("550 5.1.1", "%s: User missing home directory", name);
1065 		return;
1066 	}
1067 	else if (sm_snprintf(path, sizeof(path), "%s/%s",
1068 			     user.mbdb_homedir, HomeMailFile) >= sizeof(path))
1069 	{
1070 		exitval = EX_UNAVAILABLE;
1071 		mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
1072 		return;
1073 	}
1074 
1075 
1076 	/*
1077 	**  If the mailbox is linked or a symlink, fail.  There's an obvious
1078 	**  race here, that the file was replaced with a symbolic link after
1079 	**  the lstat returned, but before the open.  We attempt to detect
1080 	**  this by comparing the original stat information and information
1081 	**  returned by an fstat of the file descriptor returned by the open.
1082 	**
1083 	**  NB: this is a symptom of a larger problem, that the mail spooling
1084 	**  directory is writeable by the wrong users.  If that directory is
1085 	**  writeable, system security is compromised for other reasons, and
1086 	**  it cannot be fixed here.
1087 	**
1088 	**  If we created the mailbox, set the owner/group.  If that fails,
1089 	**  just return.  Another process may have already opened it, so we
1090 	**  can't unlink it.  Historically, binmail set the owner/group at
1091 	**  each mail delivery.  We no longer do this, assuming that if the
1092 	**  ownership or permissions were changed there was a reason.
1093 	**
1094 	**  XXX
1095 	**  open(2) should support flock'ing the file.
1096 	*/
1097 
1098 tryagain:
1099 #ifdef MAILLOCK
1100 	p = name;
1101 #else /* MAILLOCK */
1102 	p = path;
1103 #endif /* MAILLOCK */
1104 	if ((off = lockmbox(p)) != 0)
1105 	{
1106 		if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL)
1107 		{
1108 			ExitVal = EX_TEMPFAIL;
1109 			errcode = "451 4.3.0";
1110 		}
1111 		else
1112 			errcode = "551 5.3.0";
1113 
1114 		mailerr(errcode, "lockmailbox %s failed; error code %d %s",
1115 			p, off, errno > 0 ? sm_errstring(errno) : "");
1116 		return;
1117 	}
1118 
1119 	if (lstat(path, &sb) < 0)
1120 	{
1121 		int save_errno;
1122 		int mode = S_IRUSR|S_IWUSR;
1123 		gid_t gid = user.mbdb_gid;
1124 
1125 #ifdef MAILGID
1126 		(void) umask(0007);
1127 		gid = MAILGID;
1128 		mode |= S_IRGRP|S_IWGRP;
1129 #endif /* MAILGID */
1130 
1131 		mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY,
1132 			    mode);
1133 		save_errno = errno;
1134 
1135 		if (lstat(path, &sb) < 0)
1136 		{
1137 			ExitVal = EX_CANTCREAT;
1138 			mailerr("550 5.2.0",
1139 				"%s: lstat: file changed after open", path);
1140 			goto err1;
1141 		}
1142 		if (mbfd < 0)
1143 		{
1144 			if (save_errno == EEXIST)
1145 				goto tryagain;
1146 
1147 			/* open failed, don't try again */
1148 			mailerr("450 4.2.0", "%s: %s", path,
1149 				sm_errstring(save_errno));
1150 			goto err0;
1151 		}
1152 		else if (fchown(mbfd, user.mbdb_uid, gid) < 0)
1153 		{
1154 			mailerr("451 4.3.0", "chown %u.%u: %s",
1155 				user.mbdb_uid, gid, name);
1156 			goto err1;
1157 		}
1158 		else
1159 		{
1160 			/*
1161 			**  open() was successful, now close it so can
1162 			**  be opened as the right owner again.
1163 			**  Paranoia: reset mbdf since the file descriptor
1164 			**  is no longer valid; better safe than sorry.
1165 			*/
1166 
1167 			sb.st_uid = user.mbdb_uid;
1168 			(void) close(mbfd);
1169 			mbfd = -1;
1170 		}
1171 	}
1172 	else if (sb.st_nlink != 1)
1173 	{
1174 		mailerr("550 5.2.0", "%s: too many links", path);
1175 		goto err0;
1176 	}
1177 	else if (!S_ISREG(sb.st_mode))
1178 	{
1179 		mailerr("550 5.2.0", "%s: irregular file", path);
1180 		goto err0;
1181 	}
1182 	else if (sb.st_uid != user.mbdb_uid)
1183 	{
1184 		ExitVal = EX_CANTCREAT;
1185 		mailerr("550 5.2.0", "%s: wrong ownership (%d)",
1186 			path, (int) sb.st_uid);
1187 		goto err0;
1188 	}
1189 
1190 	/* change UID for quota checks */
1191 	if (setreuid(0, user.mbdb_uid) < 0)
1192 	{
1193 		mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
1194 			(int) user.mbdb_uid, sm_errstring(errno),
1195 			(int) getuid(), (int) geteuid());
1196 		goto err1;
1197 	}
1198 #ifdef DEBUG
1199 	fprintf(stderr, "new euid = %d\n", (int) geteuid());
1200 #endif /* DEBUG */
1201 	mbfd = open(path, O_APPEND|O_WRONLY, 0);
1202 	if (mbfd < 0)
1203 	{
1204 		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
1205 		goto err0;
1206 	}
1207 	else if (fstat(mbfd, &fsb) < 0 ||
1208 		 fsb.st_nlink != 1 ||
1209 		 sb.st_nlink != 1 ||
1210 		 !S_ISREG(fsb.st_mode) ||
1211 		 sb.st_dev != fsb.st_dev ||
1212 		 sb.st_ino != fsb.st_ino ||
1213 # if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
1214 		 sb.st_gen != fsb.st_gen ||
1215 # endif /* HAS_ST_GEN && 0 */
1216 		 sb.st_uid != fsb.st_uid)
1217 	{
1218 		ExitVal = EX_TEMPFAIL;
1219 		mailerr("550 5.2.0", "%s: fstat: file changed after open",
1220 			path);
1221 		goto err1;
1222 	}
1223 
1224 #if 0
1225 	/*
1226 	**  This code could be reused if we decide to add a
1227 	**  per-user quota field to the sm_mbdb interface.
1228 	*/
1229 
1230 	/*
1231 	**  Fail if the user has a quota specified, and delivery of this
1232 	**  message would exceed that quota.  We bounce such failures using
1233 	**  EX_UNAVAILABLE, unless there were internal problems, since
1234 	**  storing immense messages for later retries can cause queueing
1235 	**  issues.
1236 	*/
1237 
1238 	if (ui.quota > 0)
1239 	{
1240 		struct stat dsb;
1241 
1242 		if (fstat(fd, &dsb) < 0)
1243 		{
1244 			ExitVal = EX_TEMPFAIL;
1245 			mailerr("451 4.3.0",
1246 				"%s: fstat: can't stat temporary storage: %s",
1247 				ui.mailspool, sm_errstring(errno));
1248 			goto err1;
1249 		}
1250 
1251 		if (dsb.st_size + sb.st_size + 1 > ui.quota)
1252 		{
1253 			ExitVal = EX_UNAVAILABLE;
1254 			mailerr("551 5.2.2",
1255 				"%s: Mailbox full or quota exceeded",
1256 				ui.mailspool);
1257 			goto err1;
1258 		}
1259 	}
1260 #endif /* 0 */
1261 
1262 	/* Wait until we can get a lock on the file. */
1263 	if (flock(mbfd, LOCK_EX) < 0)
1264 	{
1265 		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
1266 		goto err1;
1267 	}
1268 
1269 	/* Get the starting offset of the new message */
1270 	curoff = lseek(mbfd, (off_t) 0, SEEK_END);
1271 
1272 	if (!nobiff)
1273 	{
1274 		(void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n",
1275 				   name, (LONGLONG_T) curoff);
1276 	}
1277 
1278 	/* Copy the message into the file. */
1279 	if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1)
1280 	{
1281 		mailerr("450 4.2.0", "Temporary file: %s",
1282 			sm_errstring(errno));
1283 		goto err1;
1284 	}
1285 #ifdef DEBUG
1286 	fprintf(stderr, "before writing: euid = %d\n", (int) geteuid());
1287 #endif /* DEBUG */
1288 #ifdef CONTENTLENGTH
1289 	headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ;
1290 	for (;;)
1291 	{
1292 		if (headerbytes == 0)
1293 		{
1294 			(void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr);
1295 			nr = strlen(buf);
1296 			headerbytes = -1;
1297 			readamount = 0;
1298 		}
1299 		else if (headerbytes > sizeof(buf) || headerbytes < 0)
1300 			readamount = sizeof(buf);
1301 		else
1302 			readamount = headerbytes;
1303 		if (readamount != 0)
1304 			nr = read(fd, buf, readamount);
1305 		if (nr <= 0)
1306 			break;
1307 		if (headerbytes > 0)
1308 			headerbytes -= nr ;
1309 
1310 #else /* CONTENTLENGTH */
1311 	while ((nr = read(fd, buf, sizeof(buf))) > 0)
1312 	{
1313 #endif /* CONTENTLENGTH */
1314 		for (off = 0; off < nr; off += nw)
1315 		{
1316 			if ((nw = write(mbfd, buf + off, nr - off)) < 0)
1317 			{
1318 				errcode = "450 4.2.0";
1319 #ifdef EDQUOT
1320 				if (errno == EDQUOT && BounceQuota)
1321 					errcode = "552 5.2.2";
1322 #endif /* EDQUOT */
1323 				mailerr(errcode, "%s: %s",
1324 					path, sm_errstring(errno));
1325 				goto err3;
1326 			}
1327 		}
1328 	}
1329 	if (nr < 0)
1330 	{
1331 		mailerr("450 4.2.0", "Temporary file: %s",
1332 			sm_errstring(errno));
1333 		goto err3;
1334 	}
1335 
1336 	/* Flush to disk, don't wait for update. */
1337 	if (!nofsync && fsync(mbfd) < 0)
1338 	{
1339 		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
1340 err3:
1341 #ifdef DEBUG
1342 		fprintf(stderr, "reset euid = %d\n", (int) geteuid());
1343 #endif /* DEBUG */
1344 		if (mbfd >= 0)
1345 			(void) ftruncate(mbfd, curoff);
1346 err1:		if (mbfd >= 0)
1347 			(void) close(mbfd);
1348 err0:		(void) setreuid(0, 0);
1349 		unlockmbox();
1350 		return;
1351 	}
1352 
1353 	/*
1354 	**  Save the current size so if the close() fails below
1355 	**  we can make sure no other process has changed the mailbox
1356 	**  between the failed close and the re-open()/re-lock().
1357 	**  If something else has changed the size, we shouldn't
1358 	**  try to truncate it as we may do more harm then good
1359 	**  (e.g., truncate a later message delivery).
1360 	*/
1361 
1362 	if (fstat(mbfd, &sb) < 0)
1363 		cursize = 0;
1364 	else
1365 		cursize = sb.st_size;
1366 
1367 
1368 	/* Close and check -- NFS doesn't write until the close. */
1369 	if (close(mbfd))
1370 	{
1371 		errcode = "450 4.2.0";
1372 #ifdef EDQUOT
1373 		if (errno == EDQUOT && BounceQuota)
1374 			errcode = "552 5.2.2";
1375 #endif /* EDQUOT */
1376 		mailerr(errcode, "%s: %s", path, sm_errstring(errno));
1377 		mbfd = open(path, O_WRONLY, 0);
1378 		if (mbfd < 0 ||
1379 		    cursize == 0
1380 		    || flock(mbfd, LOCK_EX) < 0 ||
1381 		    fstat(mbfd, &sb) < 0 ||
1382 		    sb.st_size != cursize ||
1383 		    sb.st_nlink != 1 ||
1384 		    !S_ISREG(sb.st_mode) ||
1385 		    sb.st_dev != fsb.st_dev ||
1386 		    sb.st_ino != fsb.st_ino ||
1387 # if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
1388 		    sb.st_gen != fsb.st_gen ||
1389 # endif /* HAS_ST_GEN && 0 */
1390 		    sb.st_uid != fsb.st_uid
1391 		   )
1392 		{
1393 			/* Don't use a bogus file */
1394 			if (mbfd >= 0)
1395 			{
1396 				(void) close(mbfd);
1397 				mbfd = -1;
1398 			}
1399 		}
1400 
1401 		/* Attempt to truncate back to pre-write size */
1402 		goto err3;
1403 	}
1404 	else if (!nobiff)
1405 		notifybiff(biffmsg);
1406 
1407 	if (setreuid(0, 0) < 0)
1408 	{
1409 		mailerr("450 4.2.0", "setreuid(0, 0): %s",
1410 			sm_errstring(errno));
1411 		goto err0;
1412 	}
1413 #ifdef DEBUG
1414 	fprintf(stderr, "reset euid = %d\n", (int) geteuid());
1415 #endif /* DEBUG */
1416 	unlockmbox();
1417 	if (LMTPMode)
1418 		printf("250 2.1.5 %s Ok\r\n", name);
1419 }
1420 
1421 /*
1422 **  user.lock files are necessary for compatibility with other
1423 **  systems, e.g., when the mail spool file is NFS exported.
1424 **  Alas, mailbox locking is more than just a local matter.
1425 **  EPA 11/94.
1426 */
1427 
1428 bool	Locked = false;
1429 
1430 #ifdef MAILLOCK
1431 int
1432 lockmbox(name)
1433 	char *name;
1434 {
1435 	int r = 0;
1436 
1437 	if (Locked)
1438 		return 0;
1439 	if ((r = maillock(name, 15)) == L_SUCCESS)
1440 	{
1441 		Locked = true;
1442 		return 0;
1443 	}
1444 	switch (r)
1445 	{
1446 	  case L_TMPLOCK:	/* Can't create tmp file */
1447 	  case L_TMPWRITE:	/* Can't write pid into lockfile */
1448 	  case L_MAXTRYS:	/* Failed after retrycnt attempts */
1449 		errno = 0;
1450 		r = EX_TEMPFAIL;
1451 		break;
1452 	  case L_ERROR:		/* Check errno for reason */
1453 		r = errno;
1454 		break;
1455 	  default:		/* other permanent errors */
1456 		errno = 0;
1457 		r = EX_UNAVAILABLE;
1458 		break;
1459 	}
1460 	return r;
1461 }
1462 
1463 void
1464 unlockmbox()
1465 {
1466 	if (Locked)
1467 		mailunlock();
1468 	Locked = false;
1469 }
1470 #else /* MAILLOCK */
1471 
1472 char	LockName[MAXPATHLEN];
1473 
1474 int
1475 lockmbox(path)
1476 	char *path;
1477 {
1478 	int statfailed = 0;
1479 	time_t start;
1480 
1481 	if (Locked)
1482 		return 0;
1483 	if (strlen(path) + 6 > sizeof LockName)
1484 		return EX_SOFTWARE;
1485 	(void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path);
1486 	(void) time(&start);
1487 	for (; ; sleep(5))
1488 	{
1489 		int fd;
1490 		struct stat st;
1491 		time_t now;
1492 
1493 		/* global timeout */
1494 		(void) time(&now);
1495 		if (now > start + LOCKTO_GLOB)
1496 		{
1497 			errno = 0;
1498 			return EX_TEMPFAIL;
1499 		}
1500 		fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, LOCKFILE_PMODE);
1501 		if (fd >= 0)
1502 		{
1503 			/* defeat lock checking programs which test pid */
1504 			(void) write(fd, "0", 2);
1505 			Locked = true;
1506 			(void) close(fd);
1507 			return 0;
1508 		}
1509 		if (stat(LockName, &st) < 0)
1510 		{
1511 			if (statfailed++ > 5)
1512 			{
1513 				errno = 0;
1514 				return EX_TEMPFAIL;
1515 			}
1516 			continue;
1517 		}
1518 		statfailed = 0;
1519 		(void) time(&now);
1520 		if (now < st.st_ctime + LOCKTO_RM)
1521 			continue;
1522 
1523 		/* try to remove stale lockfile */
1524 		if (unlink(LockName) < 0)
1525 			return errno;
1526 	}
1527 }
1528 
1529 void
1530 unlockmbox()
1531 {
1532 	if (!Locked)
1533 		return;
1534 	(void) unlink(LockName);
1535 	Locked = false;
1536 }
1537 #endif /* MAILLOCK */
1538 
1539 void
1540 notifybiff(msg)
1541 	char *msg;
1542 {
1543 	static bool initialized = false;
1544 	static int f = -1;
1545 	struct hostent *hp;
1546 	struct servent *sp;
1547 	int len;
1548 	static struct sockaddr_in addr;
1549 
1550 	if (!initialized)
1551 	{
1552 		initialized = true;
1553 
1554 		/* Be silent if biff service not available. */
1555 		if ((sp = getservbyname("biff", "udp")) == NULL ||
1556 		    (hp = gethostbyname("localhost")) == NULL ||
1557 		    hp->h_length != INADDRSZ)
1558 			return;
1559 
1560 		addr.sin_family = hp->h_addrtype;
1561 		memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ);
1562 		addr.sin_port = sp->s_port;
1563 	}
1564 
1565 	/* No message, just return */
1566 	if (msg == NULL)
1567 		return;
1568 
1569 	/* Couldn't initialize addr struct */
1570 	if (addr.sin_family == AF_UNSPEC)
1571 		return;
1572 
1573 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1574 		return;
1575 	len = strlen(msg) + 1;
1576 	(void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr));
1577 }
1578 
1579 void
1580 usage()
1581 {
1582 	ExitVal = EX_USAGE;
1583 	mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ...");
1584 	sm_exit(ExitVal);
1585 }
1586 
1587 void
1588 /*VARARGS2*/
1589 #ifdef __STDC__
1590 mailerr(const char *hdr, const char *fmt, ...)
1591 #else /* __STDC__ */
1592 mailerr(hdr, fmt, va_alist)
1593 	const char *hdr;
1594 	const char *fmt;
1595 	va_dcl
1596 #endif /* __STDC__ */
1597 {
1598 	size_t len = 0;
1599 	SM_VA_LOCAL_DECL
1600 
1601 	(void) e_to_sys(errno);
1602 
1603 	SM_VA_START(ap, fmt);
1604 
1605 	if (LMTPMode && hdr != NULL)
1606 	{
1607 		sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr);
1608 		len = strlen(ErrBuf);
1609 	}
1610 	(void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap);
1611 	SM_VA_END(ap);
1612 
1613 	if (!HoldErrs)
1614 		flush_error();
1615 
1616 	/* Log the message to syslog. */
1617 	if (!LMTPMode)
1618 		syslog(LOG_ERR, "%s", ErrBuf);
1619 }
1620 
1621 void
1622 flush_error()
1623 {
1624 	if (LMTPMode)
1625 		printf("%s\r\n", ErrBuf);
1626 	else
1627 	{
1628 		if (ExitVal != EX_USAGE)
1629 			(void) fprintf(stderr, "mail.local: ");
1630 		fprintf(stderr, "%s\n", ErrBuf);
1631 	}
1632 }
1633 
1634 #if HASHSPOOL
1635 const char *
1636 hashname(name)
1637 	char *name;
1638 {
1639 	static char p[MAXPATHLEN];
1640 	int i;
1641 	int len;
1642 	char *str;
1643 # if HASHSPOOLMD5
1644 	char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_";
1645 	MD5_CTX ctx;
1646 	unsigned char md5[18];
1647 #  if MAXPATHLEN <= 24
1648     ERROR _MAXPATHLEN <= 24
1649 #  endif /* MAXPATHLEN <= 24 */
1650 	char b64[24];
1651 	MD5_LONG bits;
1652 	int j;
1653 # endif /* HASHSPOOLMD5 */
1654 
1655 	if (HashType == HASH_NONE || HashDepth * 2 >= MAXPATHLEN)
1656 	{
1657 		p[0] = '\0';
1658 		return p;
1659 	}
1660 
1661 	switch(HashType)
1662 	{
1663 	  case HASH_USER:
1664 		str = name;
1665 		break;
1666 
1667 # if HASHSPOOLMD5
1668 	  case HASH_MD5:
1669 		MD5_Init(&ctx);
1670 		MD5_Update(&ctx, name, strlen(name));
1671 		MD5_Final(md5, &ctx);
1672 		md5[16] = 0;
1673 		md5[17] = 0;
1674 
1675 		for (i = 0; i < 6; i++)
1676 		{
1677 			bits = (unsigned) md5[(3 * i)] << 16;
1678 			bits |= (unsigned) md5[(3 * i) + 1] << 8;
1679 			bits |= (unsigned) md5[(3 * i) + 2];
1680 
1681 			for (j = 3; j >= 0; j--)
1682 			{
1683 				b64[(4 * i) + j] = Base64[(bits & 0x3f)];
1684 				bits >>= 6;
1685 			}
1686 		}
1687 		b64[22] = '\0';
1688 		str = b64;
1689 		break;
1690 # endif /* HASHSPOOLMD5 */
1691 	}
1692 
1693 	len = strlen(str);
1694 	for (i = 0; i < HashDepth; i++)
1695 	{
1696 		if (i < len)
1697 			p[i * 2] = str[i];
1698 		else
1699 			p[i * 2] = '_';
1700 		p[(i * 2) + 1] = '/';
1701 	}
1702 	p[HashDepth * 2] = '\0';
1703 	return p;
1704 }
1705 #endif /* HASHSPOOL */
1706 
1707 /*
1708  * e_to_sys --
1709  *	Guess which errno's are temporary.  Gag me.
1710  */
1711 
1712 int
1713 e_to_sys(num)
1714 	int num;
1715 {
1716 	/* Temporary failures override hard errors. */
1717 	if (ExitVal == EX_TEMPFAIL)
1718 		return ExitVal;
1719 
1720 	switch (num)		/* Hopefully temporary errors. */
1721 	{
1722 #ifdef EDQUOT
1723 	  case EDQUOT:		/* Disc quota exceeded */
1724 		if (BounceQuota)
1725 		{
1726 			ExitVal = EX_UNAVAILABLE;
1727 			break;
1728 		}
1729 		/* FALLTHROUGH */
1730 #endif /* EDQUOT */
1731 #ifdef EAGAIN
1732 	  case EAGAIN:		/* Resource temporarily unavailable */
1733 #endif /* EAGAIN */
1734 #ifdef EBUSY
1735 	  case EBUSY:		/* Device busy */
1736 #endif /* EBUSY */
1737 #ifdef EPROCLIM
1738 	  case EPROCLIM:	/* Too many processes */
1739 #endif /* EPROCLIM */
1740 #ifdef EUSERS
1741 	  case EUSERS:		/* Too many users */
1742 #endif /* EUSERS */
1743 #ifdef ECONNABORTED
1744 	  case ECONNABORTED:	/* Software caused connection abort */
1745 #endif /* ECONNABORTED */
1746 #ifdef ECONNREFUSED
1747 	  case ECONNREFUSED:	/* Connection refused */
1748 #endif /* ECONNREFUSED */
1749 #ifdef ECONNRESET
1750 	  case ECONNRESET:	/* Connection reset by peer */
1751 #endif /* ECONNRESET */
1752 #ifdef EDEADLK
1753 	  case EDEADLK:		/* Resource deadlock avoided */
1754 #endif /* EDEADLK */
1755 #ifdef EFBIG
1756 	  case EFBIG:		/* File too large */
1757 #endif /* EFBIG */
1758 #ifdef EHOSTDOWN
1759 	  case EHOSTDOWN:	/* Host is down */
1760 #endif /* EHOSTDOWN */
1761 #ifdef EHOSTUNREACH
1762 	  case EHOSTUNREACH:	/* No route to host */
1763 #endif /* EHOSTUNREACH */
1764 #ifdef EMFILE
1765 	  case EMFILE:		/* Too many open files */
1766 #endif /* EMFILE */
1767 #ifdef ENETDOWN
1768 	  case ENETDOWN:	/* Network is down */
1769 #endif /* ENETDOWN */
1770 #ifdef ENETRESET
1771 	  case ENETRESET:	/* Network dropped connection on reset */
1772 #endif /* ENETRESET */
1773 #ifdef ENETUNREACH
1774 	  case ENETUNREACH:	/* Network is unreachable */
1775 #endif /* ENETUNREACH */
1776 #ifdef ENFILE
1777 	  case ENFILE:		/* Too many open files in system */
1778 #endif /* ENFILE */
1779 #ifdef ENOBUFS
1780 	  case ENOBUFS:		/* No buffer space available */
1781 #endif /* ENOBUFS */
1782 #ifdef ENOMEM
1783 	  case ENOMEM:		/* Cannot allocate memory */
1784 #endif /* ENOMEM */
1785 #ifdef ENOSPC
1786 	  case ENOSPC:		/* No space left on device */
1787 #endif /* ENOSPC */
1788 #ifdef EROFS
1789 	  case EROFS:		/* Read-only file system */
1790 #endif /* EROFS */
1791 #ifdef ESTALE
1792 	  case ESTALE:		/* Stale NFS file handle */
1793 #endif /* ESTALE */
1794 #ifdef ETIMEDOUT
1795 	  case ETIMEDOUT:	/* Connection timed out */
1796 #endif /* ETIMEDOUT */
1797 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
1798 	  case EWOULDBLOCK:	/* Operation would block. */
1799 #endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */
1800 		ExitVal = EX_TEMPFAIL;
1801 		break;
1802 
1803 	  default:
1804 		ExitVal = EX_UNAVAILABLE;
1805 		break;
1806 	}
1807 	return ExitVal;
1808 }
1809 
1810 #if defined(ultrix) || defined(_CRAY)
1811 /*
1812  * Copyright (c) 1987, 1993
1813  *	The Regents of the University of California.  All rights reserved.
1814  *
1815  * Redistribution and use in source and binary forms, with or without
1816  * modification, are permitted provided that the following conditions
1817  * are met:
1818  * 1. Redistributions of source code must retain the above copyright
1819  *    notice, this list of conditions and the following disclaimer.
1820  * 2. Redistributions in binary form must reproduce the above copyright
1821  *    notice, this list of conditions and the following disclaimer in the
1822  *    documentation and/or other materials provided with the distribution.
1823  * 3. All advertising materials mentioning features or use of this software
1824  *    must display the following acknowledgement:
1825  *	This product includes software developed by the University of
1826  *	California, Berkeley and its contributors.
1827  * 4. Neither the name of the University nor the names of its contributors
1828  *    may be used to endorse or promote products derived from this software
1829  *    without specific prior written permission.
1830  *
1831  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1832  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1833  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1834  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1835  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1836  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1837  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1838  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1839  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1840  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1841  * SUCH DAMAGE.
1842  */
1843 
1844 # if defined(LIBC_SCCS) && !defined(lint)
1845 static char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
1846 # endif /* defined(LIBC_SCCS) && !defined(lint) */
1847 
1848 # include <sys/types.h>
1849 # include <sys/stat.h>
1850 # include <fcntl.h>
1851 # include <errno.h>
1852 # include <stdio.h>
1853 # include <ctype.h>
1854 
1855 static int _gettemp();
1856 
1857 mkstemp(path)
1858 	char *path;
1859 {
1860 	int fd;
1861 
1862 	return (_gettemp(path, &fd) ? fd : -1);
1863 }
1864 
1865 static
1866 _gettemp(path, doopen)
1867 	char *path;
1868 	register int *doopen;
1869 {
1870 	extern int errno;
1871 	register char *start, *trv;
1872 	struct stat sbuf;
1873 	unsigned int pid;
1874 
1875 	pid = getpid();
1876 	for (trv = path; *trv; ++trv);		/* extra X's get set to 0's */
1877 	while (*--trv == 'X')
1878 	{
1879 		*trv = (pid % 10) + '0';
1880 		pid /= 10;
1881 	}
1882 
1883 	/*
1884 	 * check the target directory; if you have six X's and it
1885 	 * doesn't exist this runs for a *very* long time.
1886 	 */
1887 	for (start = trv + 1;; --trv)
1888 	{
1889 		if (trv <= path)
1890 			break;
1891 		if (*trv == '/')
1892 		{
1893 			*trv = '\0';
1894 			if (stat(path, &sbuf) < 0)
1895 				return(0);
1896 			if (!S_ISDIR(sbuf.st_mode))
1897 			{
1898 				errno = ENOTDIR;
1899 				return(0);
1900 			}
1901 			*trv = '/';
1902 			break;
1903 		}
1904 	}
1905 
1906 	for (;;)
1907 	{
1908 		if (doopen)
1909 		{
1910 			if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR,
1911 					    0600)) >= 0)
1912 				return(1);
1913 			if (errno != EEXIST)
1914 				return(0);
1915 		}
1916 		else if (stat(path, &sbuf) < 0)
1917 			return(errno == ENOENT ? 1 : 0);
1918 
1919 		/* tricky little algorithm for backward compatibility */
1920 		for (trv = start;;)
1921 		{
1922 			if (!*trv)
1923 				return(0);
1924 			if (*trv == 'z')
1925 				*trv++ = 'a';
1926 			else
1927 			{
1928 				if (isascii(*trv) && isdigit(*trv))
1929 					*trv = 'a';
1930 				else
1931 					++*trv;
1932 				break;
1933 			}
1934 		}
1935 	}
1936 	/* NOTREACHED */
1937 }
1938 #endif /* defined(ultrix) || defined(_CRAY) */
1939