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