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