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