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