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