xref: /freebsd/contrib/sendmail/mail.local/mail.local.c (revision d615a19295d5853e057b4992d71f968df849ad96)
1c2aa98e2SPeter Wemm /*-
2c2aa98e2SPeter Wemm  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3c2aa98e2SPeter Wemm  * Copyright (c) 1990, 1993, 1994
4c2aa98e2SPeter Wemm  *	The Regents of the University of California.  All rights reserved.
5c2aa98e2SPeter Wemm  *
6c2aa98e2SPeter Wemm  * By using this file, you agree to the terms and conditions set
7c2aa98e2SPeter Wemm  * forth in the LICENSE file which can be found at the top level of
8c2aa98e2SPeter Wemm  * the sendmail distribution.
9c2aa98e2SPeter Wemm  *
10c2aa98e2SPeter Wemm  */
11c2aa98e2SPeter Wemm 
12c2aa98e2SPeter Wemm #ifndef lint
13c2aa98e2SPeter Wemm static char copyright[] =
14c2aa98e2SPeter Wemm "@(#) Copyright (c) 1990, 1993, 1994\n\
15c2aa98e2SPeter Wemm 	The Regents of the University of California.  All rights reserved.\n";
16c2aa98e2SPeter Wemm #endif /* not lint */
17c2aa98e2SPeter Wemm 
18c2aa98e2SPeter Wemm #ifndef lint
19c2aa98e2SPeter Wemm static char sccsid[] = "@(#)mail.local.c	8.78 (Berkeley) 5/19/98";
20c2aa98e2SPeter Wemm #endif /* not lint */
21c2aa98e2SPeter Wemm 
22c2aa98e2SPeter Wemm /*
23c2aa98e2SPeter Wemm  * This is not intended to work on System V derived systems
24c2aa98e2SPeter Wemm  * such as Solaris or HP-UX, since they use a totally different
25c2aa98e2SPeter Wemm  * approach to mailboxes (essentially, they have a setgid program
26c2aa98e2SPeter Wemm  * rather than setuid, and they rely on the ability to "give away"
27c2aa98e2SPeter Wemm  * files to do their work).  IT IS NOT A BUG that this doesn't
28c2aa98e2SPeter Wemm  * work on such architectures.
29c2aa98e2SPeter Wemm  */
30c2aa98e2SPeter Wemm 
31c2aa98e2SPeter Wemm #include <sys/param.h>
32c2aa98e2SPeter Wemm #include <sys/stat.h>
33c2aa98e2SPeter Wemm #include <sys/socket.h>
34c2aa98e2SPeter Wemm #include <sys/file.h>
35c2aa98e2SPeter Wemm 
36c2aa98e2SPeter Wemm #include <netinet/in.h>
37c2aa98e2SPeter Wemm 
38c2aa98e2SPeter Wemm #include <errno.h>
39c2aa98e2SPeter Wemm #include <fcntl.h>
40c2aa98e2SPeter Wemm #include <netdb.h>
41c2aa98e2SPeter Wemm #include <pwd.h>
42c2aa98e2SPeter Wemm #include <stdio.h>
43c2aa98e2SPeter Wemm #include <stdlib.h>
44c2aa98e2SPeter Wemm #include <string.h>
45c2aa98e2SPeter Wemm #include <syslog.h>
46c2aa98e2SPeter Wemm #include <time.h>
47c2aa98e2SPeter Wemm #include <unistd.h>
48c2aa98e2SPeter Wemm #ifdef EX_OK
49c2aa98e2SPeter Wemm # undef EX_OK		/* unistd.h may have another use for this */
50c2aa98e2SPeter Wemm #endif
51c2aa98e2SPeter Wemm #include <sysexits.h>
52c2aa98e2SPeter Wemm #include <ctype.h>
53c2aa98e2SPeter Wemm 
54c2aa98e2SPeter Wemm #ifdef __STDC__
55c2aa98e2SPeter Wemm #include <stdarg.h>
56c2aa98e2SPeter Wemm #else
57c2aa98e2SPeter Wemm #include <varargs.h>
58c2aa98e2SPeter Wemm #endif
59c2aa98e2SPeter Wemm 
60c2aa98e2SPeter Wemm #if (defined(sun) && defined(__svr4__)) || defined(__SVR4)
61c2aa98e2SPeter Wemm # define USE_LOCKF	1
62c2aa98e2SPeter Wemm # define USE_SETEUID	1
63c2aa98e2SPeter Wemm # define _PATH_MAILDIR	"/var/mail"
64c2aa98e2SPeter Wemm #endif
65c2aa98e2SPeter Wemm 
66c2aa98e2SPeter Wemm #if (defined(sun) && !defined(__svr4__)) && !defined(__SVR4)
67c2aa98e2SPeter Wemm # ifdef __dead
68c2aa98e2SPeter Wemm #  undef __dead
69c2aa98e2SPeter Wemm #  define __dead
70c2aa98e2SPeter Wemm # endif
71c2aa98e2SPeter Wemm #endif
72c2aa98e2SPeter Wemm 
73c2aa98e2SPeter Wemm #if defined(_AIX)
74c2aa98e2SPeter Wemm # define USE_LOCKF	1
75c2aa98e2SPeter Wemm # define USE_SETEUID	1
76c2aa98e2SPeter Wemm # define USE_VSYSLOG	0
77c2aa98e2SPeter Wemm #endif
78c2aa98e2SPeter Wemm 
79c2aa98e2SPeter Wemm #if defined(__hpux)
80c2aa98e2SPeter Wemm # define USE_LOCKF	1
81c2aa98e2SPeter Wemm # define USE_SETRESUID	1
82c2aa98e2SPeter Wemm # define USE_VSYSLOG	0
83c2aa98e2SPeter Wemm # ifdef __dead
84c2aa98e2SPeter Wemm #  undef __dead
85c2aa98e2SPeter Wemm #  define __dead
86c2aa98e2SPeter Wemm # endif
87c2aa98e2SPeter Wemm #endif
88c2aa98e2SPeter Wemm 
89c2aa98e2SPeter Wemm #if defined(_CRAY)
90c2aa98e2SPeter Wemm # if !defined(MAXPATHLEN)
91c2aa98e2SPeter Wemm #  define MAXPATHLEN PATHSIZE
92c2aa98e2SPeter Wemm # endif
93c2aa98e2SPeter Wemm # define USE_VSYSLOG   0
94c2aa98e2SPeter Wemm # define _PATH_MAILDIR	"/usr/spool/mail"
95c2aa98e2SPeter Wemm #endif
96c2aa98e2SPeter Wemm 
97c2aa98e2SPeter Wemm #if defined(ultrix)
98c2aa98e2SPeter Wemm # define USE_VSYSLOG	0
99c2aa98e2SPeter Wemm #endif
100c2aa98e2SPeter Wemm 
101c2aa98e2SPeter Wemm #if defined(__osf__)
102c2aa98e2SPeter Wemm # define USE_VSYSLOG	0
103c2aa98e2SPeter Wemm #endif
104c2aa98e2SPeter Wemm 
105c2aa98e2SPeter Wemm #if defined(NeXT)
106c2aa98e2SPeter Wemm # include <libc.h>
107c2aa98e2SPeter Wemm # define _PATH_MAILDIR	"/usr/spool/mail"
108c2aa98e2SPeter Wemm # define __dead		/* empty */
109c2aa98e2SPeter Wemm # define S_IRUSR	S_IREAD
110c2aa98e2SPeter Wemm # define S_IWUSR	S_IWRITE
111c2aa98e2SPeter Wemm #endif
112c2aa98e2SPeter Wemm 
113c2aa98e2SPeter Wemm #if defined(IRIX64) || defined(IRIX5) || defined(IRIX6)
114c2aa98e2SPeter Wemm # include <paths.h>
115c2aa98e2SPeter Wemm # define HASSTRERROR	1	/* has strerror(3) */
116c2aa98e2SPeter Wemm #endif
117c2aa98e2SPeter Wemm 
118c2aa98e2SPeter Wemm /*
119c2aa98e2SPeter Wemm  * If you don't have flock, you could try using lockf instead.
120c2aa98e2SPeter Wemm  */
121c2aa98e2SPeter Wemm 
122c2aa98e2SPeter Wemm #ifdef USE_LOCKF
123c2aa98e2SPeter Wemm # define flock(a, b)	lockf(a, b, 0)
124c2aa98e2SPeter Wemm # define LOCK_EX	F_LOCK
125c2aa98e2SPeter Wemm #endif
126c2aa98e2SPeter Wemm 
127c2aa98e2SPeter Wemm #ifndef USE_VSYSLOG
128c2aa98e2SPeter Wemm # define USE_VSYSLOG	1
129c2aa98e2SPeter Wemm #endif
130c2aa98e2SPeter Wemm 
131c2aa98e2SPeter Wemm #ifndef LOCK_EX
132c2aa98e2SPeter Wemm # include <sys/file.h>
133c2aa98e2SPeter Wemm #endif
134c2aa98e2SPeter Wemm 
135c2aa98e2SPeter Wemm #if defined(BSD4_4) || defined(__GLIBC__)
136c2aa98e2SPeter Wemm # include "pathnames.h"
137c2aa98e2SPeter Wemm #endif
138c2aa98e2SPeter Wemm 
139c2aa98e2SPeter Wemm #ifndef __P
140c2aa98e2SPeter Wemm # ifdef __STDC__
141c2aa98e2SPeter Wemm #  define __P(protos)	protos
142c2aa98e2SPeter Wemm # else
143c2aa98e2SPeter Wemm #  define __P(protos)	()
144c2aa98e2SPeter Wemm #  define const
145c2aa98e2SPeter Wemm # endif
146c2aa98e2SPeter Wemm #endif
147c2aa98e2SPeter Wemm #ifndef __dead
148c2aa98e2SPeter Wemm # if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__)
149c2aa98e2SPeter Wemm #  define __dead	__volatile
150c2aa98e2SPeter Wemm # else
151c2aa98e2SPeter Wemm #  define __dead
152c2aa98e2SPeter Wemm # endif
153c2aa98e2SPeter Wemm #endif
154c2aa98e2SPeter Wemm 
155c2aa98e2SPeter Wemm #ifdef BSD4_4
156c2aa98e2SPeter Wemm # define HAS_ST_GEN	1
157c2aa98e2SPeter Wemm #else
158c2aa98e2SPeter Wemm # ifndef _BSD_VA_LIST_
159c2aa98e2SPeter Wemm #  define _BSD_VA_LIST_	va_list
160c2aa98e2SPeter Wemm # endif
161c2aa98e2SPeter Wemm #endif
162c2aa98e2SPeter Wemm 
163c2aa98e2SPeter Wemm #if defined(BSD4_4) || defined(linux)
164c2aa98e2SPeter Wemm # define HASSNPRINTF	1
165c2aa98e2SPeter Wemm #else
166c2aa98e2SPeter Wemm # ifndef ultrix
167c2aa98e2SPeter Wemm extern FILE	*fdopen __P((int, const char *));
168c2aa98e2SPeter Wemm # endif
169c2aa98e2SPeter Wemm #endif
170c2aa98e2SPeter Wemm 
171c2aa98e2SPeter Wemm #if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206)
172c2aa98e2SPeter Wemm # define HASSNPRINTF	1		/* has snprintf starting in 2.6 */
173c2aa98e2SPeter Wemm #endif
174c2aa98e2SPeter Wemm 
175c2aa98e2SPeter Wemm #if !HASSNPRINTF
176c2aa98e2SPeter Wemm extern int	snprintf __P((char *, size_t, const char *, ...));
177c2aa98e2SPeter Wemm # ifndef _CRAY
178c2aa98e2SPeter Wemm extern int	vsnprintf __P((char *, size_t, const char *, ...));
179c2aa98e2SPeter Wemm # endif
180c2aa98e2SPeter Wemm #endif
181c2aa98e2SPeter Wemm 
182c2aa98e2SPeter Wemm #if defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__)
183c2aa98e2SPeter Wemm # ifndef HASSTRERROR
184c2aa98e2SPeter Wemm #  define HASSTRERROR	1
185c2aa98e2SPeter Wemm # endif
186c2aa98e2SPeter Wemm #endif
187c2aa98e2SPeter Wemm 
188c2aa98e2SPeter Wemm #if !HASSTRERROR
189c2aa98e2SPeter Wemm extern char	*strerror __P((int));
190c2aa98e2SPeter Wemm #endif
191c2aa98e2SPeter Wemm 
192c2aa98e2SPeter Wemm /*
193c2aa98e2SPeter Wemm  * If you don't have setreuid, and you have saved uids, and you have
194c2aa98e2SPeter Wemm  * a seteuid() call that doesn't try to emulate using setuid(), then
195c2aa98e2SPeter Wemm  * you can try defining USE_SETEUID.
196c2aa98e2SPeter Wemm  */
197c2aa98e2SPeter Wemm #ifdef USE_SETEUID
198c2aa98e2SPeter Wemm # define setreuid(r, e)		seteuid(e)
199c2aa98e2SPeter Wemm #endif
200c2aa98e2SPeter Wemm 
201c2aa98e2SPeter Wemm /*
202c2aa98e2SPeter Wemm  * And of course on hpux you have setresuid()
203c2aa98e2SPeter Wemm  */
204c2aa98e2SPeter Wemm #ifdef USE_SETRESUID
205c2aa98e2SPeter Wemm # define setreuid(r, e)		setresuid(-1, e, -1)
206c2aa98e2SPeter Wemm #endif
207c2aa98e2SPeter Wemm 
208c2aa98e2SPeter Wemm #ifndef _PATH_LOCTMP
209c2aa98e2SPeter Wemm # define _PATH_LOCTMP	"/tmp/local.XXXXXX"
210c2aa98e2SPeter Wemm #endif
211c2aa98e2SPeter Wemm #ifndef _PATH_MAILDIR
212c2aa98e2SPeter Wemm # define _PATH_MAILDIR	"/var/spool/mail"
213c2aa98e2SPeter Wemm #endif
214c2aa98e2SPeter Wemm 
215c2aa98e2SPeter Wemm #ifndef S_ISREG
216c2aa98e2SPeter Wemm # define S_ISREG(mode)	(((mode) & _S_IFMT) == S_IFREG)
217c2aa98e2SPeter Wemm #endif
218c2aa98e2SPeter Wemm 
219c2aa98e2SPeter Wemm int	eval = EX_OK;			/* sysexits.h error value. */
220c2aa98e2SPeter Wemm int	lmtpmode = 0;
221c2aa98e2SPeter Wemm u_char	tTdvect[100];
222c2aa98e2SPeter Wemm 
223d615a192SPeter Wemm void		deliver __P((int, char *, int, int));
224c2aa98e2SPeter Wemm void		e_to_sys __P((int));
225d615a192SPeter Wemm void		err __P((const char *, ...)) __dead2;
226c2aa98e2SPeter Wemm void		notifybiff __P((char *));
227c2aa98e2SPeter Wemm int		store __P((char *, int));
228c2aa98e2SPeter Wemm void		usage __P((void));
229c2aa98e2SPeter Wemm void		vwarn __P((const char *, _BSD_VA_LIST_));
230c2aa98e2SPeter Wemm void		warn __P((const char *, ...));
231c2aa98e2SPeter Wemm void		lockmbox __P((char *));
232c2aa98e2SPeter Wemm void		unlockmbox __P((void));
233c2aa98e2SPeter Wemm void		mailerr __P((const char *, const char *, ...));
234d615a192SPeter Wemm void		dolmtp __P((int, int));
235c2aa98e2SPeter Wemm 
236c2aa98e2SPeter Wemm int
237c2aa98e2SPeter Wemm main(argc, argv)
238c2aa98e2SPeter Wemm 	int argc;
239c2aa98e2SPeter Wemm 	char *argv[];
240c2aa98e2SPeter Wemm {
241c2aa98e2SPeter Wemm 	struct passwd *pw;
242d615a192SPeter Wemm 	int ch, fd, nobiff, nofsync;
243c2aa98e2SPeter Wemm 	uid_t uid;
244c2aa98e2SPeter Wemm 	char *from;
245c2aa98e2SPeter Wemm 	extern char *optarg;
246c2aa98e2SPeter Wemm 	extern int optind;
247c2aa98e2SPeter Wemm 
248c2aa98e2SPeter Wemm 	/* make sure we have some open file descriptors */
249c2aa98e2SPeter Wemm 	for (fd = 10; fd < 30; fd++)
250c2aa98e2SPeter Wemm 		(void) close(fd);
251c2aa98e2SPeter Wemm 
252c2aa98e2SPeter Wemm 	/* use a reasonable umask */
253c2aa98e2SPeter Wemm 	(void) umask(0077);
254c2aa98e2SPeter Wemm 
255c2aa98e2SPeter Wemm #ifdef LOG_MAIL
256c2aa98e2SPeter Wemm 	openlog("mail.local", 0, LOG_MAIL);
257c2aa98e2SPeter Wemm #else
258c2aa98e2SPeter Wemm 	openlog("mail.local", 0);
259c2aa98e2SPeter Wemm #endif
260c2aa98e2SPeter Wemm 
261c2aa98e2SPeter Wemm 	from = NULL;
262d615a192SPeter Wemm 	nobiff = 0;
263d615a192SPeter Wemm 	nofsync = 0;
264d615a192SPeter Wemm 	while ((ch = getopt(argc, argv, "bdf:r:ls")) != -1)
265c2aa98e2SPeter Wemm 		switch(ch) {
266d615a192SPeter Wemm 		case 'b':
267d615a192SPeter Wemm 			nobiff++;
268d615a192SPeter Wemm 			break;
269c2aa98e2SPeter Wemm 		case 'd':		/* Backward compatible. */
270c2aa98e2SPeter Wemm 			break;
271c2aa98e2SPeter Wemm 		case 'f':
272c2aa98e2SPeter Wemm 		case 'r':		/* Backward compatible. */
273c2aa98e2SPeter Wemm 			if (from != NULL) {
274c2aa98e2SPeter Wemm 				warn("multiple -f options");
275c2aa98e2SPeter Wemm 				usage();
276c2aa98e2SPeter Wemm 			}
277c2aa98e2SPeter Wemm 			from = optarg;
278c2aa98e2SPeter Wemm 			break;
279c2aa98e2SPeter Wemm 		case 'l':
280c2aa98e2SPeter Wemm 			lmtpmode++;
281c2aa98e2SPeter Wemm 			break;
282d615a192SPeter Wemm 		case 's':
283d615a192SPeter Wemm 			nofsync++;
284d615a192SPeter Wemm 			break;
285c2aa98e2SPeter Wemm 		case '?':
286c2aa98e2SPeter Wemm 		default:
287c2aa98e2SPeter Wemm 			usage();
288c2aa98e2SPeter Wemm 		}
289c2aa98e2SPeter Wemm 	argc -= optind;
290c2aa98e2SPeter Wemm 	argv += optind;
291c2aa98e2SPeter Wemm 
292c2aa98e2SPeter Wemm 	if (lmtpmode)
293d615a192SPeter Wemm 		dolmtp(nobiff, nofsync);
294c2aa98e2SPeter Wemm 
295c2aa98e2SPeter Wemm 	if (!*argv)
296c2aa98e2SPeter Wemm 		usage();
297c2aa98e2SPeter Wemm 
298c2aa98e2SPeter Wemm 	/*
299c2aa98e2SPeter Wemm 	 * If from not specified, use the name from getlogin() if the
300c2aa98e2SPeter Wemm 	 * uid matches, otherwise, use the name from the password file
301c2aa98e2SPeter Wemm 	 * corresponding to the uid.
302c2aa98e2SPeter Wemm 	 */
303c2aa98e2SPeter Wemm 	uid = getuid();
304c2aa98e2SPeter Wemm 	if (!from && (!(from = getlogin()) ||
305c2aa98e2SPeter Wemm 	    !(pw = getpwnam(from)) || pw->pw_uid != uid))
306c2aa98e2SPeter Wemm 		from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
307c2aa98e2SPeter Wemm 
308c2aa98e2SPeter Wemm 	/*
309c2aa98e2SPeter Wemm 	 * There is no way to distinguish the error status of one delivery
310c2aa98e2SPeter Wemm 	 * from the rest of the deliveries.  So, if we failed hard on one
311c2aa98e2SPeter Wemm 	 * or more deliveries, but had no failures on any of the others, we
312c2aa98e2SPeter Wemm 	 * return a hard failure.  If we failed temporarily on one or more
313c2aa98e2SPeter Wemm 	 * deliveries, we return a temporary failure regardless of the other
314c2aa98e2SPeter Wemm 	 * failures.  This results in the delivery being reattempted later
315c2aa98e2SPeter Wemm 	 * at the expense of repeated failures and multiple deliveries.
316c2aa98e2SPeter Wemm 	 */
317c2aa98e2SPeter Wemm 	for (fd = store(from, 0); *argv; ++argv)
318d615a192SPeter Wemm 		deliver(fd, *argv, nobiff, nofsync);
319c2aa98e2SPeter Wemm 	exit(eval);
320c2aa98e2SPeter Wemm }
321c2aa98e2SPeter Wemm 
322c2aa98e2SPeter Wemm char *
323c2aa98e2SPeter Wemm parseaddr(s)
324c2aa98e2SPeter Wemm 	char *s;
325c2aa98e2SPeter Wemm {
326c2aa98e2SPeter Wemm 	char *p;
327c2aa98e2SPeter Wemm 	int len;
328c2aa98e2SPeter Wemm 
329c2aa98e2SPeter Wemm 	if (*s++ != '<')
330c2aa98e2SPeter Wemm 		return NULL;
331c2aa98e2SPeter Wemm 
332c2aa98e2SPeter Wemm 	p = s;
333c2aa98e2SPeter Wemm 
334c2aa98e2SPeter Wemm 	/* at-domain-list */
335c2aa98e2SPeter Wemm 	while (*p == '@') {
336c2aa98e2SPeter Wemm 		p++;
337c2aa98e2SPeter Wemm 		if (*p == '[') {
338c2aa98e2SPeter Wemm 			p++;
339c2aa98e2SPeter Wemm 			while (isascii(*p) &&
340c2aa98e2SPeter Wemm 			       (isalnum(*p) || *p == '.' ||
341c2aa98e2SPeter Wemm 				*p == '-' || *p == ':'))
342c2aa98e2SPeter Wemm 				p++;
343c2aa98e2SPeter Wemm 			if (*p++ != ']')
344c2aa98e2SPeter Wemm 				return NULL;
345c2aa98e2SPeter Wemm 		} else {
346c2aa98e2SPeter Wemm 			while ((isascii(*p) && isalnum(*p)) ||
347c2aa98e2SPeter Wemm 			       *p == '.' || *p == '-')
348c2aa98e2SPeter Wemm 				p++;
349c2aa98e2SPeter Wemm 		}
350c2aa98e2SPeter Wemm 		if (*p == ',' && p[1] == '@')
351c2aa98e2SPeter Wemm 			p++;
352c2aa98e2SPeter Wemm 		else if (*p == ':' && p[1] != '@')
353c2aa98e2SPeter Wemm 			p++;
354c2aa98e2SPeter Wemm 		else
355c2aa98e2SPeter Wemm 			return NULL;
356c2aa98e2SPeter Wemm 	}
357c2aa98e2SPeter Wemm 
358c2aa98e2SPeter Wemm 	/* local-part */
359c2aa98e2SPeter Wemm 	if (*p == '\"') {
360c2aa98e2SPeter Wemm 		p++;
361c2aa98e2SPeter Wemm 		while (*p && *p != '\"') {
362c2aa98e2SPeter Wemm 			if (*p == '\\') {
363c2aa98e2SPeter Wemm 				if (!*++p)
364c2aa98e2SPeter Wemm 					return NULL;
365c2aa98e2SPeter Wemm 			}
366c2aa98e2SPeter Wemm 			p++;
367c2aa98e2SPeter Wemm 		}
368c2aa98e2SPeter Wemm 		if (!*p++)
369c2aa98e2SPeter Wemm 			return NULL;
370c2aa98e2SPeter Wemm 	} else {
371c2aa98e2SPeter Wemm 		while (*p && *p != '@' && *p != '>') {
372c2aa98e2SPeter Wemm 			if (*p == '\\') {
373c2aa98e2SPeter Wemm 				if (!*++p)
374c2aa98e2SPeter Wemm 					return NULL;
375c2aa98e2SPeter Wemm 			} else {
376c2aa98e2SPeter Wemm 			if (*p <= ' ' || (*p & 128) ||
377c2aa98e2SPeter Wemm 			    strchr("<>()[]\\,;:\"", *p))
378c2aa98e2SPeter Wemm 				return NULL;
379c2aa98e2SPeter Wemm 			}
380c2aa98e2SPeter Wemm 			p++;
381c2aa98e2SPeter Wemm 		}
382c2aa98e2SPeter Wemm 	}
383c2aa98e2SPeter Wemm 
384c2aa98e2SPeter Wemm 	/* @domain */
385c2aa98e2SPeter Wemm 	if (*p == '@') {
386c2aa98e2SPeter Wemm 		p++;
387c2aa98e2SPeter Wemm 		if (*p == '[') {
388c2aa98e2SPeter Wemm 			p++;
389c2aa98e2SPeter Wemm 			while (isascii(*p) &&
390c2aa98e2SPeter Wemm 			       (isalnum(*p) || *p == '.' ||
391c2aa98e2SPeter Wemm 				*p == '-' || *p == ':'))
392c2aa98e2SPeter Wemm 				p++;
393c2aa98e2SPeter Wemm 			if (*p++ != ']')
394c2aa98e2SPeter Wemm 				return NULL;
395c2aa98e2SPeter Wemm 		} else {
396c2aa98e2SPeter Wemm 			while ((isascii(*p) && isalnum(*p)) ||
397c2aa98e2SPeter Wemm 			       *p == '.' || *p == '-')
398c2aa98e2SPeter Wemm 				p++;
399c2aa98e2SPeter Wemm 		}
400c2aa98e2SPeter Wemm 	}
401c2aa98e2SPeter Wemm 
402c2aa98e2SPeter Wemm 	if (*p++ != '>')
403c2aa98e2SPeter Wemm 		return NULL;
404c2aa98e2SPeter Wemm 	if (*p && *p != ' ')
405c2aa98e2SPeter Wemm 		return NULL;
406c2aa98e2SPeter Wemm 	len = p - s - 1;
407c2aa98e2SPeter Wemm 
408c2aa98e2SPeter Wemm 	p = malloc(len + 1);
409c2aa98e2SPeter Wemm 	if (p == NULL) {
410c2aa98e2SPeter Wemm 		printf("421 4.3.0 memory exhausted\r\n");
411c2aa98e2SPeter Wemm 		exit(EX_TEMPFAIL);
412c2aa98e2SPeter Wemm 	}
413c2aa98e2SPeter Wemm 
414c2aa98e2SPeter Wemm 	strncpy(p, s, len);
415c2aa98e2SPeter Wemm 	p[len] = '\0';
416c2aa98e2SPeter Wemm 	return p;
417c2aa98e2SPeter Wemm }
418c2aa98e2SPeter Wemm 
419c2aa98e2SPeter Wemm char *
420c2aa98e2SPeter Wemm process_recipient(addr)
421c2aa98e2SPeter Wemm 	char *addr;
422c2aa98e2SPeter Wemm {
423c2aa98e2SPeter Wemm 	if (getpwnam(addr) == NULL) {
424c2aa98e2SPeter Wemm 		return "550 5.1.1 user unknown";
425c2aa98e2SPeter Wemm 	}
426c2aa98e2SPeter Wemm 
427c2aa98e2SPeter Wemm 	return NULL;
428c2aa98e2SPeter Wemm }
429c2aa98e2SPeter Wemm 
430c2aa98e2SPeter Wemm 
431c2aa98e2SPeter Wemm #define RCPT_GROW	30
432c2aa98e2SPeter Wemm 
433c2aa98e2SPeter Wemm void
434d615a192SPeter Wemm dolmtp(nobiff, nofsync)
435d615a192SPeter Wemm 	int nobiff, nofsync;
436c2aa98e2SPeter Wemm {
437c2aa98e2SPeter Wemm 	char *return_path = NULL;
438c2aa98e2SPeter Wemm 	char **rcpt_addr = NULL;
439c2aa98e2SPeter Wemm 	int rcpt_num = 0;
440c2aa98e2SPeter Wemm 	int rcpt_alloc = 0;
441c2aa98e2SPeter Wemm 	char myhostname[1024];
442c2aa98e2SPeter Wemm 	char buf[4096];
443c2aa98e2SPeter Wemm 	char *err;
444c2aa98e2SPeter Wemm 	int msgfd;
445c2aa98e2SPeter Wemm 	char *p;
446c2aa98e2SPeter Wemm 	int i;
447c2aa98e2SPeter Wemm 
448c2aa98e2SPeter Wemm 	gethostname(myhostname, sizeof myhostname - 1);
449c2aa98e2SPeter Wemm 
450c2aa98e2SPeter Wemm 	printf("220 %s LMTP ready\r\n", myhostname);
451c2aa98e2SPeter Wemm 	for (;;) {
452c2aa98e2SPeter Wemm 		fflush(stdout);
453c2aa98e2SPeter Wemm 		if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
454c2aa98e2SPeter Wemm 			exit(EX_OK);
455c2aa98e2SPeter Wemm 		}
456c2aa98e2SPeter Wemm 		p = buf + strlen(buf) - 1;
457c2aa98e2SPeter Wemm 		if (p >= buf && *p == '\n')
458c2aa98e2SPeter Wemm 			*p-- = '\0';
459c2aa98e2SPeter Wemm 		if (p >= buf && *p == '\r')
460c2aa98e2SPeter Wemm 			*p-- = '\0';
461c2aa98e2SPeter Wemm 
462c2aa98e2SPeter Wemm 		switch (buf[0]) {
463c2aa98e2SPeter Wemm 
464c2aa98e2SPeter Wemm 		case 'd':
465c2aa98e2SPeter Wemm 		case 'D':
466c2aa98e2SPeter Wemm 			if (strcasecmp(buf, "data") == 0) {
467c2aa98e2SPeter Wemm 				if (rcpt_num == 0) {
468c2aa98e2SPeter Wemm 					printf("503 5.5.1 No recipients\r\n");
469c2aa98e2SPeter Wemm 					continue;
470c2aa98e2SPeter Wemm 				}
471c2aa98e2SPeter Wemm 				msgfd = store(return_path, rcpt_num);
472c2aa98e2SPeter Wemm 				if (msgfd == -1)
473c2aa98e2SPeter Wemm 					continue;
474c2aa98e2SPeter Wemm 
475c2aa98e2SPeter Wemm 				for (i = 0; i < rcpt_num; i++) {
476c2aa98e2SPeter Wemm 					p = strchr(rcpt_addr[i], '+');
477c2aa98e2SPeter Wemm 					if (p != NULL)
478c2aa98e2SPeter Wemm 						*p++ = '\0';
479d615a192SPeter Wemm 					deliver(msgfd, rcpt_addr[i], nobiff,
480d615a192SPeter Wemm 					    nofsync);
481c2aa98e2SPeter Wemm 				}
482c2aa98e2SPeter Wemm 				close(msgfd);
483c2aa98e2SPeter Wemm 				goto rset;
484c2aa98e2SPeter Wemm 			}
485c2aa98e2SPeter Wemm 			goto syntaxerr;
486c2aa98e2SPeter Wemm 
487c2aa98e2SPeter Wemm 		case 'l':
488c2aa98e2SPeter Wemm 		case 'L':
489c2aa98e2SPeter Wemm 			if (strncasecmp(buf, "lhlo ", 5) == 0) {
490c2aa98e2SPeter Wemm 				printf("250-%s\r\n250-8BITMIME\r\n250-ENHANCEDSTATUSCODES\r\n250 PIPELINING\r\n",
491c2aa98e2SPeter Wemm 					   myhostname);
492c2aa98e2SPeter Wemm 				continue;
493c2aa98e2SPeter Wemm 			}
494c2aa98e2SPeter Wemm 			goto syntaxerr;
495c2aa98e2SPeter Wemm 
496c2aa98e2SPeter Wemm 		case 'm':
497c2aa98e2SPeter Wemm 		case 'M':
498c2aa98e2SPeter Wemm 			if (strncasecmp(buf, "mail ", 5) == 0) {
499c2aa98e2SPeter Wemm 				if (return_path != NULL) {
500c2aa98e2SPeter Wemm 					printf("503 5.5.1 Nested MAIL command\r\n");
501c2aa98e2SPeter Wemm 					continue;
502c2aa98e2SPeter Wemm 				}
503c2aa98e2SPeter Wemm 				if (strncasecmp(buf+5, "from:", 5) != 0 ||
504c2aa98e2SPeter Wemm 				    ((return_path = parseaddr(buf+10)) == NULL)) {
505c2aa98e2SPeter Wemm 					printf("501 5.5.4 Syntax error in parameters\r\n");
506c2aa98e2SPeter Wemm 					continue;
507c2aa98e2SPeter Wemm 				}
508c2aa98e2SPeter Wemm 				printf("250 2.5.0 ok\r\n");
509c2aa98e2SPeter Wemm 				continue;
510c2aa98e2SPeter Wemm 			}
511c2aa98e2SPeter Wemm 			goto syntaxerr;
512c2aa98e2SPeter Wemm 
513c2aa98e2SPeter Wemm 		case 'n':
514c2aa98e2SPeter Wemm 		case 'N':
515c2aa98e2SPeter Wemm 			if (strcasecmp(buf, "noop") == 0) {
516c2aa98e2SPeter Wemm 				printf("250 2.0.0 ok\r\n");
517c2aa98e2SPeter Wemm 				continue;
518c2aa98e2SPeter Wemm 			}
519c2aa98e2SPeter Wemm 			goto syntaxerr;
520c2aa98e2SPeter Wemm 
521c2aa98e2SPeter Wemm 		case 'q':
522c2aa98e2SPeter Wemm 		case 'Q':
523c2aa98e2SPeter Wemm 			if (strcasecmp(buf, "quit") == 0) {
524c2aa98e2SPeter Wemm 				printf("221 2.0.0 bye\r\n");
525c2aa98e2SPeter Wemm 				exit(EX_OK);
526c2aa98e2SPeter Wemm 			}
527c2aa98e2SPeter Wemm 			goto syntaxerr;
528c2aa98e2SPeter Wemm 
529c2aa98e2SPeter Wemm 		case 'r':
530c2aa98e2SPeter Wemm 		case 'R':
531c2aa98e2SPeter Wemm 			if (strncasecmp(buf, "rcpt ", 5) == 0) {
532c2aa98e2SPeter Wemm 				if (return_path == NULL) {
533c2aa98e2SPeter Wemm 					printf("503 5.5.1 Need MAIL command\r\n");
534c2aa98e2SPeter Wemm 					continue;
535c2aa98e2SPeter Wemm 				}
536c2aa98e2SPeter Wemm 				if (rcpt_num >= rcpt_alloc) {
537c2aa98e2SPeter Wemm 					rcpt_alloc += RCPT_GROW;
538c2aa98e2SPeter Wemm 					rcpt_addr = (char **)
539c2aa98e2SPeter Wemm 						realloc((char *)rcpt_addr,
540c2aa98e2SPeter Wemm 							rcpt_alloc * sizeof(char **));
541c2aa98e2SPeter Wemm 					if (rcpt_addr == NULL) {
542c2aa98e2SPeter Wemm 						printf("421 4.3.0 memory exhausted\r\n");
543c2aa98e2SPeter Wemm 						exit(EX_TEMPFAIL);
544c2aa98e2SPeter Wemm 					}
545c2aa98e2SPeter Wemm 				}
546c2aa98e2SPeter Wemm 				if (strncasecmp(buf+5, "to:", 3) != 0 ||
547c2aa98e2SPeter Wemm 				    ((rcpt_addr[rcpt_num] = parseaddr(buf+8)) == NULL)) {
548c2aa98e2SPeter Wemm 					printf("501 5.5.4 Syntax error in parameters\r\n");
549c2aa98e2SPeter Wemm 					continue;
550c2aa98e2SPeter Wemm 				}
551c2aa98e2SPeter Wemm 				if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL) {
552c2aa98e2SPeter Wemm 					printf("%s\r\n", err);
553c2aa98e2SPeter Wemm 					continue;
554c2aa98e2SPeter Wemm 				}
555c2aa98e2SPeter Wemm 				rcpt_num++;
556c2aa98e2SPeter Wemm 				printf("250 2.1.5 ok\r\n");
557c2aa98e2SPeter Wemm 				continue;
558c2aa98e2SPeter Wemm 			}
559c2aa98e2SPeter Wemm 			else if (strcasecmp(buf, "rset") == 0) {
560c2aa98e2SPeter Wemm 				printf("250 2.0.0 ok\r\n");
561c2aa98e2SPeter Wemm 
562c2aa98e2SPeter Wemm   rset:
563c2aa98e2SPeter Wemm 				while (rcpt_num) {
564c2aa98e2SPeter Wemm 					free(rcpt_addr[--rcpt_num]);
565c2aa98e2SPeter Wemm 				}
566c2aa98e2SPeter Wemm 				if (return_path != NULL)
567c2aa98e2SPeter Wemm 					free(return_path);
568c2aa98e2SPeter Wemm 				return_path = NULL;
569c2aa98e2SPeter Wemm 				continue;
570c2aa98e2SPeter Wemm 			}
571c2aa98e2SPeter Wemm 			goto syntaxerr;
572c2aa98e2SPeter Wemm 
573c2aa98e2SPeter Wemm 		case 'v':
574c2aa98e2SPeter Wemm 		case 'V':
575c2aa98e2SPeter Wemm 			if (strncasecmp(buf, "vrfy ", 5) == 0) {
576c2aa98e2SPeter Wemm 				printf("252 2.3.3 try RCPT to attempt delivery\r\n");
577c2aa98e2SPeter Wemm 				continue;
578c2aa98e2SPeter Wemm 			}
579c2aa98e2SPeter Wemm 			goto syntaxerr;
580c2aa98e2SPeter Wemm 
581c2aa98e2SPeter Wemm 		default:
582c2aa98e2SPeter Wemm   syntaxerr:
583c2aa98e2SPeter Wemm 			printf("500 5.5.2 Syntax error\r\n");
584c2aa98e2SPeter Wemm 			continue;
585c2aa98e2SPeter Wemm 		}
586c2aa98e2SPeter Wemm 	}
587c2aa98e2SPeter Wemm }
588c2aa98e2SPeter Wemm 
589c2aa98e2SPeter Wemm int
590c2aa98e2SPeter Wemm store(from, lmtprcpts)
591c2aa98e2SPeter Wemm 	char *from;
592c2aa98e2SPeter Wemm 	int lmtprcpts;
593c2aa98e2SPeter Wemm {
594c2aa98e2SPeter Wemm 	FILE *fp;
595c2aa98e2SPeter Wemm 	time_t tval;
596c2aa98e2SPeter Wemm 	int fd, eline;
597c2aa98e2SPeter Wemm 	char line[2048];
598c2aa98e2SPeter Wemm 	char tmpbuf[sizeof _PATH_LOCTMP + 1];
599c2aa98e2SPeter Wemm 
600c2aa98e2SPeter Wemm 	strcpy(tmpbuf, _PATH_LOCTMP);
601c2aa98e2SPeter Wemm 	if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
602c2aa98e2SPeter Wemm 		if (lmtprcpts) {
603c2aa98e2SPeter Wemm 			printf("451 4.3.0 unable to open temporary file\r\n");
604c2aa98e2SPeter Wemm 			return -1;
605c2aa98e2SPeter Wemm 		} else {
606c2aa98e2SPeter Wemm 			e_to_sys(errno);
607c2aa98e2SPeter Wemm 			err("unable to open temporary file");
608c2aa98e2SPeter Wemm 		}
609c2aa98e2SPeter Wemm 	}
610c2aa98e2SPeter Wemm 	(void)unlink(tmpbuf);
611c2aa98e2SPeter Wemm 
612c2aa98e2SPeter Wemm 	if (lmtpmode) {
613c2aa98e2SPeter Wemm 		printf("354 go ahead\r\n");
614c2aa98e2SPeter Wemm 		fflush(stdout);
615c2aa98e2SPeter Wemm 	}
616c2aa98e2SPeter Wemm 
617c2aa98e2SPeter Wemm 	(void)time(&tval);
618c2aa98e2SPeter Wemm 	(void)fprintf(fp, "From %s %s", from, ctime(&tval));
619c2aa98e2SPeter Wemm 
620c2aa98e2SPeter Wemm 	line[0] = '\0';
621c2aa98e2SPeter Wemm 	for (eline = 1; fgets(line, sizeof(line), stdin);) {
622c2aa98e2SPeter Wemm 		if (line[strlen(line)-2] == '\r') {
623c2aa98e2SPeter Wemm 			strcpy(line+strlen(line)-2, "\n");
624c2aa98e2SPeter Wemm 		}
625c2aa98e2SPeter Wemm 		if (lmtprcpts && line[0] == '.') {
626c2aa98e2SPeter Wemm 			if (line[1] == '\n')
627c2aa98e2SPeter Wemm 				goto lmtpdot;
628c2aa98e2SPeter Wemm 			strcpy(line, line+1);
629c2aa98e2SPeter Wemm 		}
630c2aa98e2SPeter Wemm 		if (line[0] == '\n')
631c2aa98e2SPeter Wemm 			eline = 1;
632c2aa98e2SPeter Wemm 		else {
633c2aa98e2SPeter Wemm 			if (eline && line[0] == 'F' &&
634c2aa98e2SPeter Wemm 			    !memcmp(line, "From ", 5))
635c2aa98e2SPeter Wemm 				(void)putc('>', fp);
636c2aa98e2SPeter Wemm 			eline = 0;
637c2aa98e2SPeter Wemm 		}
638c2aa98e2SPeter Wemm 		(void)fprintf(fp, "%s", line);
639c2aa98e2SPeter Wemm 		if (ferror(fp)) {
640c2aa98e2SPeter Wemm 			if (lmtprcpts) {
641c2aa98e2SPeter Wemm 				while (lmtprcpts--) {
642c2aa98e2SPeter Wemm 					printf("451 4.3.0 temporary file write error\r\n");
643c2aa98e2SPeter Wemm 				}
644c2aa98e2SPeter Wemm 				fclose(fp);
645c2aa98e2SPeter Wemm 				return -1;
646c2aa98e2SPeter Wemm 			} else {
647c2aa98e2SPeter Wemm 				e_to_sys(errno);
648c2aa98e2SPeter Wemm 				err("temporary file write error");
649c2aa98e2SPeter Wemm 			}
650c2aa98e2SPeter Wemm 		}
651c2aa98e2SPeter Wemm 	}
652c2aa98e2SPeter Wemm 
653c2aa98e2SPeter Wemm 	if (lmtprcpts) {
654c2aa98e2SPeter Wemm 		/* Got a premature EOF -- toss message and exit */
655c2aa98e2SPeter Wemm 		exit(EX_OK);
656c2aa98e2SPeter Wemm 	}
657c2aa98e2SPeter Wemm 
658c2aa98e2SPeter Wemm 	/* If message not newline terminated, need an extra. */
659c2aa98e2SPeter Wemm 	if (strchr(line, '\n') == NULL)
660c2aa98e2SPeter Wemm 		(void)putc('\n', fp);
661c2aa98e2SPeter Wemm 
662c2aa98e2SPeter Wemm   lmtpdot:
663c2aa98e2SPeter Wemm 
664c2aa98e2SPeter Wemm 	/* Output a newline; note, empty messages are allowed. */
665c2aa98e2SPeter Wemm 	(void)putc('\n', fp);
666c2aa98e2SPeter Wemm 
667c2aa98e2SPeter Wemm 	if (fflush(fp) == EOF || ferror(fp)) {
668c2aa98e2SPeter Wemm 		if (lmtprcpts) {
669c2aa98e2SPeter Wemm 			while (lmtprcpts--) {
670c2aa98e2SPeter Wemm 				printf("451 4.3.0 temporary file write error\r\n");
671c2aa98e2SPeter Wemm 			}
672c2aa98e2SPeter Wemm 			fclose(fp);
673c2aa98e2SPeter Wemm 			return -1;
674c2aa98e2SPeter Wemm 		} else {
675c2aa98e2SPeter Wemm 			e_to_sys(errno);
676c2aa98e2SPeter Wemm 			err("temporary file write error");
677c2aa98e2SPeter Wemm 		}
678c2aa98e2SPeter Wemm 	}
679c2aa98e2SPeter Wemm 	return (fd);
680c2aa98e2SPeter Wemm }
681c2aa98e2SPeter Wemm 
682c2aa98e2SPeter Wemm void
683d615a192SPeter Wemm deliver(fd, name, nobiff, nofsync)
684c2aa98e2SPeter Wemm 	int fd;
685c2aa98e2SPeter Wemm 	char *name;
686d615a192SPeter Wemm 	int nobiff, nofsync;
687c2aa98e2SPeter Wemm {
688c2aa98e2SPeter Wemm 	struct stat fsb, sb;
689c2aa98e2SPeter Wemm 	struct passwd *pw;
690c2aa98e2SPeter Wemm 	int mbfd, nr, nw, off;
691c2aa98e2SPeter Wemm 	char *p;
692c2aa98e2SPeter Wemm 	char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
693c2aa98e2SPeter Wemm 	off_t curoff;
694c2aa98e2SPeter Wemm 	extern char *quad_to_string();
695c2aa98e2SPeter Wemm 
696c2aa98e2SPeter Wemm 	/*
697c2aa98e2SPeter Wemm 	 * Disallow delivery to unknown names -- special mailboxes can be
698c2aa98e2SPeter Wemm 	 * handled in the sendmail aliases file.
699c2aa98e2SPeter Wemm 	 */
700c2aa98e2SPeter Wemm 	if ((pw = getpwnam(name)) == NULL) {
701c2aa98e2SPeter Wemm 		if (eval != EX_TEMPFAIL)
702c2aa98e2SPeter Wemm 			eval = EX_UNAVAILABLE;
703c2aa98e2SPeter Wemm 		if (lmtpmode) {
704c2aa98e2SPeter Wemm 			if (eval == EX_TEMPFAIL) {
705c2aa98e2SPeter Wemm 				printf("451 4.3.0 cannot lookup name: %s\r\n", name);
706c2aa98e2SPeter Wemm 			} else {
707c2aa98e2SPeter Wemm 				printf("550 5.1.1 unknown name: %s\r\n", name);
708c2aa98e2SPeter Wemm 			}
709c2aa98e2SPeter Wemm 		}
710c2aa98e2SPeter Wemm 		else {
711c2aa98e2SPeter Wemm 			warn("unknown name: %s", name);
712c2aa98e2SPeter Wemm 		}
713c2aa98e2SPeter Wemm 		return;
714c2aa98e2SPeter Wemm 	}
715c2aa98e2SPeter Wemm 	endpwent();
716c2aa98e2SPeter Wemm 
717c2aa98e2SPeter Wemm 	/*
718c2aa98e2SPeter Wemm 	 * Keep name reasonably short to avoid buffer overruns.
719c2aa98e2SPeter Wemm 	 *	This isn't necessary on BSD because of the proper
720c2aa98e2SPeter Wemm 	 *	definition of snprintf(), but it can cause problems
721c2aa98e2SPeter Wemm 	 *	on other systems.
722c2aa98e2SPeter Wemm 	 * Also, clear out any bogus characters.
723c2aa98e2SPeter Wemm 	 */
724c2aa98e2SPeter Wemm 
725c2aa98e2SPeter Wemm 	if (strlen(name) > 40)
726c2aa98e2SPeter Wemm 		name[40] = '\0';
727c2aa98e2SPeter Wemm 	for (p = name; *p != '\0'; p++)
728c2aa98e2SPeter Wemm 	{
729c2aa98e2SPeter Wemm 		if (!isascii(*p))
730c2aa98e2SPeter Wemm 			*p &= 0x7f;
731c2aa98e2SPeter Wemm 		else if (!isprint(*p))
732c2aa98e2SPeter Wemm 			*p = '.';
733c2aa98e2SPeter Wemm 	}
734c2aa98e2SPeter Wemm 
735c2aa98e2SPeter Wemm 	(void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
736c2aa98e2SPeter Wemm 
737c2aa98e2SPeter Wemm 	/*
738c2aa98e2SPeter Wemm 	 * If the mailbox is linked or a symlink, fail.  There's an obvious
739c2aa98e2SPeter Wemm 	 * race here, that the file was replaced with a symbolic link after
740c2aa98e2SPeter Wemm 	 * the lstat returned, but before the open.  We attempt to detect
741c2aa98e2SPeter Wemm 	 * this by comparing the original stat information and information
742c2aa98e2SPeter Wemm 	 * returned by an fstat of the file descriptor returned by the open.
743c2aa98e2SPeter Wemm 	 *
744c2aa98e2SPeter Wemm 	 * NB: this is a symptom of a larger problem, that the mail spooling
745c2aa98e2SPeter Wemm 	 * directory is writeable by the wrong users.  If that directory is
746c2aa98e2SPeter Wemm 	 * writeable, system security is compromised for other reasons, and
747c2aa98e2SPeter Wemm 	 * it cannot be fixed here.
748c2aa98e2SPeter Wemm 	 *
749c2aa98e2SPeter Wemm 	 * If we created the mailbox, set the owner/group.  If that fails,
750c2aa98e2SPeter Wemm 	 * just return.  Another process may have already opened it, so we
751c2aa98e2SPeter Wemm 	 * can't unlink it.  Historically, binmail set the owner/group at
752c2aa98e2SPeter Wemm 	 * each mail delivery.  We no longer do this, assuming that if the
753c2aa98e2SPeter Wemm 	 * ownership or permissions were changed there was a reason.
754c2aa98e2SPeter Wemm 	 *
755c2aa98e2SPeter Wemm 	 * XXX
756c2aa98e2SPeter Wemm 	 * open(2) should support flock'ing the file.
757c2aa98e2SPeter Wemm 	 */
758c2aa98e2SPeter Wemm tryagain:
759c2aa98e2SPeter Wemm 	lockmbox(path);
760c2aa98e2SPeter Wemm 	if (lstat(path, &sb) < 0) {
761c2aa98e2SPeter Wemm 		mbfd = open(path,
762c2aa98e2SPeter Wemm 			O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
763c2aa98e2SPeter Wemm 		if (lstat(path, &sb) < 0)
764c2aa98e2SPeter Wemm 		{
765c2aa98e2SPeter Wemm 			eval = EX_CANTCREAT;
766c2aa98e2SPeter Wemm 			warn("%s: lstat: file changed after open", path);
767c2aa98e2SPeter Wemm 			goto err1;
768c2aa98e2SPeter Wemm 		}
769c2aa98e2SPeter Wemm 		else
770c2aa98e2SPeter Wemm 			sb.st_uid = pw->pw_uid;
771c2aa98e2SPeter Wemm 		if (mbfd == -1) {
772c2aa98e2SPeter Wemm 			if (errno == EEXIST)
773c2aa98e2SPeter Wemm 				goto tryagain;
774c2aa98e2SPeter Wemm 		} else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
775c2aa98e2SPeter Wemm 			mailerr("451 4.3.0", "chown %u.%u: %s",
776c2aa98e2SPeter Wemm 				pw->pw_uid, pw->pw_gid, name);
777c2aa98e2SPeter Wemm 			goto err1;
778c2aa98e2SPeter Wemm 		}
779c2aa98e2SPeter Wemm 	} else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
780c2aa98e2SPeter Wemm 		mailerr("550 5.2.0", "%s: irregular file", path);
781c2aa98e2SPeter Wemm 		goto err0;
782c2aa98e2SPeter Wemm 	} else if (sb.st_uid != pw->pw_uid) {
783c2aa98e2SPeter Wemm 		eval = EX_CANTCREAT;
784c2aa98e2SPeter Wemm 		mailerr("550 5.2.0", "%s: wrong ownership (%d)",
785c2aa98e2SPeter Wemm 				path, sb.st_uid);
786c2aa98e2SPeter Wemm 		goto err0;
787c2aa98e2SPeter Wemm 	} else {
788c2aa98e2SPeter Wemm 		mbfd = open(path, O_APPEND|O_WRONLY, 0);
789c2aa98e2SPeter Wemm 	}
790c2aa98e2SPeter Wemm 
791c2aa98e2SPeter Wemm 	if (mbfd == -1) {
792c2aa98e2SPeter Wemm 		mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
793c2aa98e2SPeter Wemm 		goto err0;
794c2aa98e2SPeter Wemm 	} else if (fstat(mbfd, &fsb) < 0 ||
795c2aa98e2SPeter Wemm 	    fsb.st_nlink != 1 ||
796c2aa98e2SPeter Wemm 	    sb.st_nlink != 1 ||
797c2aa98e2SPeter Wemm 	    !S_ISREG(fsb.st_mode) ||
798c2aa98e2SPeter Wemm 	    sb.st_dev != fsb.st_dev ||
799c2aa98e2SPeter Wemm 	    sb.st_ino != fsb.st_ino ||
800c2aa98e2SPeter Wemm #if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
801c2aa98e2SPeter Wemm 	    sb.st_gen != fsb.st_gen ||
802c2aa98e2SPeter Wemm #endif
803c2aa98e2SPeter Wemm 	    sb.st_uid != fsb.st_uid) {
804c2aa98e2SPeter Wemm 		eval = EX_TEMPFAIL;
805c2aa98e2SPeter Wemm 		warn("%s: fstat: file changed after open", path);
806c2aa98e2SPeter Wemm 		goto err1;
807c2aa98e2SPeter Wemm 	}
808c2aa98e2SPeter Wemm 
809c2aa98e2SPeter Wemm 	/* Wait until we can get a lock on the file. */
810c2aa98e2SPeter Wemm 	if (flock(mbfd, LOCK_EX)) {
811c2aa98e2SPeter Wemm 		mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
812c2aa98e2SPeter Wemm 		goto err1;
813c2aa98e2SPeter Wemm 	}
814c2aa98e2SPeter Wemm 
815d615a192SPeter Wemm 	if (!nobiff) {
816c2aa98e2SPeter Wemm 		/* Get the starting offset of the new message for biff. */
817c2aa98e2SPeter Wemm 		curoff = lseek(mbfd, (off_t)0, SEEK_END);
818c2aa98e2SPeter Wemm 		if (sizeof curoff > sizeof(long))
819c2aa98e2SPeter Wemm 			(void)snprintf(biffmsg, sizeof(biffmsg), "%s@%s\n",
820c2aa98e2SPeter Wemm 				       name, quad_to_string(curoff));
821c2aa98e2SPeter Wemm 		else
822c2aa98e2SPeter Wemm 			(void)snprintf(biffmsg, sizeof(biffmsg), "%s@%ld\n",
823c2aa98e2SPeter Wemm 				       name, curoff);
824d615a192SPeter Wemm 	}
825c2aa98e2SPeter Wemm 
826c2aa98e2SPeter Wemm 	/* Copy the message into the file. */
827c2aa98e2SPeter Wemm 	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
828c2aa98e2SPeter Wemm 		mailerr("450 4.2.0", "temporary file: %s",
829c2aa98e2SPeter Wemm 			strerror(errno));
830c2aa98e2SPeter Wemm 		goto err1;
831c2aa98e2SPeter Wemm 	}
832c2aa98e2SPeter Wemm 	if (setreuid(0, pw->pw_uid) < 0) {
833c2aa98e2SPeter Wemm 		mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
834c2aa98e2SPeter Wemm 		     pw->pw_uid, strerror(errno), getuid(), geteuid());
835c2aa98e2SPeter Wemm 		goto err1;
836c2aa98e2SPeter Wemm 	}
837c2aa98e2SPeter Wemm #ifdef DEBUG
838c2aa98e2SPeter Wemm 	printf("new euid = %d\n", geteuid());
839c2aa98e2SPeter Wemm #endif
840c2aa98e2SPeter Wemm 	while ((nr = read(fd, buf, sizeof(buf))) > 0)
841c2aa98e2SPeter Wemm 		for (off = 0; off < nr; off += nw)
842c2aa98e2SPeter Wemm 			if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
843c2aa98e2SPeter Wemm 				mailerr("450 4.2.0", "%s: %s",
844c2aa98e2SPeter Wemm 					path, strerror(errno));
845c2aa98e2SPeter Wemm 				goto err3;
846c2aa98e2SPeter Wemm 			}
847c2aa98e2SPeter Wemm 	if (nr < 0) {
848c2aa98e2SPeter Wemm 		mailerr("450 4.2.0", "temporary file: %s",
849c2aa98e2SPeter Wemm 			strerror(errno));
850c2aa98e2SPeter Wemm 		goto err3;
851c2aa98e2SPeter Wemm 	}
852c2aa98e2SPeter Wemm 
853c2aa98e2SPeter Wemm 	/* Flush to disk, don't wait for update. */
854d615a192SPeter Wemm 	if (!nofsync && fsync(mbfd)) {
855c2aa98e2SPeter Wemm 		mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
856c2aa98e2SPeter Wemm err3:
857c2aa98e2SPeter Wemm 		if (setreuid(0, 0) < 0) {
858c2aa98e2SPeter Wemm 			e_to_sys(errno);
859c2aa98e2SPeter Wemm 			mailerr("450 4.2.0", "setreuid(0, 0): %s",
860c2aa98e2SPeter Wemm 				strerror(errno));
861c2aa98e2SPeter Wemm 		}
862c2aa98e2SPeter Wemm #ifdef DEBUG
863c2aa98e2SPeter Wemm 		printf("reset euid = %d\n", geteuid());
864c2aa98e2SPeter Wemm #endif
865c2aa98e2SPeter Wemm 		(void)ftruncate(mbfd, curoff);
866c2aa98e2SPeter Wemm err1:		(void)close(mbfd);
867c2aa98e2SPeter Wemm err0:		unlockmbox();
868c2aa98e2SPeter Wemm 		return;
869c2aa98e2SPeter Wemm 	}
870c2aa98e2SPeter Wemm 
871c2aa98e2SPeter Wemm 	/* Close and check -- NFS doesn't write until the close. */
872c2aa98e2SPeter Wemm 	if (close(mbfd)) {
873c2aa98e2SPeter Wemm 		mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
874c2aa98e2SPeter Wemm 		truncate(path, curoff);
875d615a192SPeter Wemm 	} else if (!nobiff)
876c2aa98e2SPeter Wemm 		notifybiff(biffmsg);
877c2aa98e2SPeter Wemm 
878c2aa98e2SPeter Wemm 	if (setreuid(0, 0) < 0) {
879c2aa98e2SPeter Wemm 		mailerr("450 4.2.0", "setreuid(0, 0): %s",
880c2aa98e2SPeter Wemm 			strerror(errno));
881c2aa98e2SPeter Wemm 		goto err0;
882c2aa98e2SPeter Wemm 	}
883c2aa98e2SPeter Wemm #ifdef DEBUG
884c2aa98e2SPeter Wemm 	printf("reset euid = %d\n", geteuid());
885c2aa98e2SPeter Wemm #endif
886c2aa98e2SPeter Wemm 	unlockmbox();
887c2aa98e2SPeter Wemm 	if (lmtpmode) {
888c2aa98e2SPeter Wemm 		printf("250 2.1.5 %s OK\r\n", name);
889c2aa98e2SPeter Wemm 	}
890c2aa98e2SPeter Wemm }
891c2aa98e2SPeter Wemm 
892c2aa98e2SPeter Wemm /*
893c2aa98e2SPeter Wemm  * user.lock files are necessary for compatibility with other
894c2aa98e2SPeter Wemm  * systems, e.g., when the mail spool file is NFS exported.
895c2aa98e2SPeter Wemm  * Alas, mailbox locking is more than just a local matter.
896c2aa98e2SPeter Wemm  * EPA 11/94.
897c2aa98e2SPeter Wemm  */
898c2aa98e2SPeter Wemm 
899c2aa98e2SPeter Wemm char	lockname[MAXPATHLEN];
900c2aa98e2SPeter Wemm int	locked = 0;
901c2aa98e2SPeter Wemm 
902c2aa98e2SPeter Wemm void
903c2aa98e2SPeter Wemm lockmbox(path)
904c2aa98e2SPeter Wemm 	char *path;
905c2aa98e2SPeter Wemm {
906c2aa98e2SPeter Wemm 	int statfailed = 0;
907c2aa98e2SPeter Wemm 
908c2aa98e2SPeter Wemm 	if (locked)
909c2aa98e2SPeter Wemm 		return;
910c2aa98e2SPeter Wemm 	if (strlen(path) + 6 > sizeof lockname)
911c2aa98e2SPeter Wemm 		return;
912c2aa98e2SPeter Wemm 	snprintf(lockname, sizeof lockname, "%s.lock", path);
913c2aa98e2SPeter Wemm 	for (;; sleep(5)) {
914c2aa98e2SPeter Wemm 		int fd;
915c2aa98e2SPeter Wemm 		struct stat st;
916c2aa98e2SPeter Wemm 		time_t now;
917c2aa98e2SPeter Wemm 
918c2aa98e2SPeter Wemm 		fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0);
919c2aa98e2SPeter Wemm 		if (fd >= 0) {
920c2aa98e2SPeter Wemm 			/* defeat lock checking programs which test pid */
921c2aa98e2SPeter Wemm 			write(fd, "0", 2);
922c2aa98e2SPeter Wemm 			locked = 1;
923c2aa98e2SPeter Wemm 			close(fd);
924c2aa98e2SPeter Wemm 			return;
925c2aa98e2SPeter Wemm 		}
926c2aa98e2SPeter Wemm 		if (stat(lockname, &st) < 0) {
927c2aa98e2SPeter Wemm 			if (statfailed++ > 5)
928c2aa98e2SPeter Wemm 				return;
929c2aa98e2SPeter Wemm 			continue;
930c2aa98e2SPeter Wemm 		}
931c2aa98e2SPeter Wemm 		statfailed = 0;
932c2aa98e2SPeter Wemm 		time(&now);
933c2aa98e2SPeter Wemm 		if (now < st.st_ctime + 300)
934c2aa98e2SPeter Wemm 			continue;
935c2aa98e2SPeter Wemm 		unlink(lockname);
936c2aa98e2SPeter Wemm 	}
937c2aa98e2SPeter Wemm }
938c2aa98e2SPeter Wemm 
939c2aa98e2SPeter Wemm void
940c2aa98e2SPeter Wemm unlockmbox()
941c2aa98e2SPeter Wemm {
942c2aa98e2SPeter Wemm 	if (!locked)
943c2aa98e2SPeter Wemm 		return;
944c2aa98e2SPeter Wemm 	unlink(lockname);
945c2aa98e2SPeter Wemm 	locked = 0;
946c2aa98e2SPeter Wemm }
947c2aa98e2SPeter Wemm 
948c2aa98e2SPeter Wemm void
949c2aa98e2SPeter Wemm notifybiff(msg)
950c2aa98e2SPeter Wemm 	char *msg;
951c2aa98e2SPeter Wemm {
952c2aa98e2SPeter Wemm 	static struct sockaddr_in addr;
953c2aa98e2SPeter Wemm 	static int f = -1;
954c2aa98e2SPeter Wemm 	struct hostent *hp;
955c2aa98e2SPeter Wemm 	struct servent *sp;
956c2aa98e2SPeter Wemm 	int len;
957c2aa98e2SPeter Wemm 
958c2aa98e2SPeter Wemm 	if (addr.sin_family == 0) {
959c2aa98e2SPeter Wemm 		/* Be silent if biff service not available. */
960c2aa98e2SPeter Wemm 		if ((sp = getservbyname("biff", "udp")) == NULL)
961c2aa98e2SPeter Wemm 			return;
962c2aa98e2SPeter Wemm 		if ((hp = gethostbyname("localhost")) == NULL) {
963c2aa98e2SPeter Wemm 			warn("localhost: %s", strerror(errno));
964c2aa98e2SPeter Wemm 			return;
965c2aa98e2SPeter Wemm 		}
966c2aa98e2SPeter Wemm 		addr.sin_family = hp->h_addrtype;
967c2aa98e2SPeter Wemm 		memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
968c2aa98e2SPeter Wemm 		addr.sin_port = sp->s_port;
969c2aa98e2SPeter Wemm 	}
970c2aa98e2SPeter Wemm 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
971c2aa98e2SPeter Wemm 		warn("socket: %s", strerror(errno));
972c2aa98e2SPeter Wemm 		return;
973c2aa98e2SPeter Wemm 	}
974c2aa98e2SPeter Wemm 	len = strlen(msg) + 1;
975c2aa98e2SPeter Wemm 	if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
976c2aa98e2SPeter Wemm 	    != len)
977c2aa98e2SPeter Wemm 		warn("sendto biff: %s", strerror(errno));
978c2aa98e2SPeter Wemm }
979c2aa98e2SPeter Wemm 
980c2aa98e2SPeter Wemm void
981c2aa98e2SPeter Wemm usage()
982c2aa98e2SPeter Wemm {
983c2aa98e2SPeter Wemm 	eval = EX_USAGE;
984d615a192SPeter Wemm 	err("usage: mail.local [-b] [-l] [-f from] [-s] user ...");
985c2aa98e2SPeter Wemm }
986c2aa98e2SPeter Wemm 
987c2aa98e2SPeter Wemm void
988c2aa98e2SPeter Wemm #ifdef __STDC__
989c2aa98e2SPeter Wemm mailerr(const char *hdr, const char *fmt, ...)
990c2aa98e2SPeter Wemm #else
991c2aa98e2SPeter Wemm mailerr(hdr, fmt, va_alist)
992c2aa98e2SPeter Wemm 	const char *hdr;
993c2aa98e2SPeter Wemm 	const char *fmt;
994c2aa98e2SPeter Wemm 	va_dcl
995c2aa98e2SPeter Wemm #endif
996c2aa98e2SPeter Wemm {
997c2aa98e2SPeter Wemm 	va_list ap;
998c2aa98e2SPeter Wemm 
999c2aa98e2SPeter Wemm #ifdef __STDC__
1000c2aa98e2SPeter Wemm 	va_start(ap, fmt);
1001c2aa98e2SPeter Wemm #else
1002c2aa98e2SPeter Wemm 	va_start(ap);
1003c2aa98e2SPeter Wemm #endif
1004c2aa98e2SPeter Wemm 	if (lmtpmode)
1005c2aa98e2SPeter Wemm 	{
1006c2aa98e2SPeter Wemm 		printf("%s ", hdr);
1007c2aa98e2SPeter Wemm 		vprintf(fmt, ap);
1008c2aa98e2SPeter Wemm 		printf("\r\n");
1009c2aa98e2SPeter Wemm 	}
1010c2aa98e2SPeter Wemm 	else
1011c2aa98e2SPeter Wemm 	{
1012c2aa98e2SPeter Wemm 		e_to_sys(errno);
1013c2aa98e2SPeter Wemm 		vwarn(fmt, ap);
1014c2aa98e2SPeter Wemm 	}
1015c2aa98e2SPeter Wemm }
1016c2aa98e2SPeter Wemm 
1017c2aa98e2SPeter Wemm #ifdef __STDC__
1018c2aa98e2SPeter Wemm void
1019c2aa98e2SPeter Wemm err(const char *fmt, ...)
1020c2aa98e2SPeter Wemm #else
1021c2aa98e2SPeter Wemm void
1022c2aa98e2SPeter Wemm err(fmt, va_alist)
1023c2aa98e2SPeter Wemm 	const char *fmt;
1024c2aa98e2SPeter Wemm 	va_dcl
1025c2aa98e2SPeter Wemm #endif
1026c2aa98e2SPeter Wemm {
1027c2aa98e2SPeter Wemm 	va_list ap;
1028c2aa98e2SPeter Wemm 
1029c2aa98e2SPeter Wemm #ifdef __STDC__
1030c2aa98e2SPeter Wemm 	va_start(ap, fmt);
1031c2aa98e2SPeter Wemm #else
1032c2aa98e2SPeter Wemm 	va_start(ap);
1033c2aa98e2SPeter Wemm #endif
1034c2aa98e2SPeter Wemm 	vwarn(fmt, ap);
1035c2aa98e2SPeter Wemm 	va_end(ap);
1036c2aa98e2SPeter Wemm 
1037c2aa98e2SPeter Wemm 	exit(eval);
1038c2aa98e2SPeter Wemm }
1039c2aa98e2SPeter Wemm 
1040c2aa98e2SPeter Wemm void
1041c2aa98e2SPeter Wemm #ifdef __STDC__
1042c2aa98e2SPeter Wemm warn(const char *fmt, ...)
1043c2aa98e2SPeter Wemm #else
1044c2aa98e2SPeter Wemm warn(fmt, va_alist)
1045c2aa98e2SPeter Wemm 	const char *fmt;
1046c2aa98e2SPeter Wemm 	va_dcl
1047c2aa98e2SPeter Wemm #endif
1048c2aa98e2SPeter Wemm {
1049c2aa98e2SPeter Wemm 	va_list ap;
1050c2aa98e2SPeter Wemm 
1051c2aa98e2SPeter Wemm #ifdef __STDC__
1052c2aa98e2SPeter Wemm 	va_start(ap, fmt);
1053c2aa98e2SPeter Wemm #else
1054c2aa98e2SPeter Wemm 	va_start(ap);
1055c2aa98e2SPeter Wemm #endif
1056c2aa98e2SPeter Wemm 	vwarn(fmt, ap);
1057c2aa98e2SPeter Wemm 	va_end(ap);
1058c2aa98e2SPeter Wemm }
1059c2aa98e2SPeter Wemm 
1060c2aa98e2SPeter Wemm void
1061c2aa98e2SPeter Wemm vwarn(fmt, ap)
1062c2aa98e2SPeter Wemm 	const char *fmt;
1063c2aa98e2SPeter Wemm 	_BSD_VA_LIST_ ap;
1064c2aa98e2SPeter Wemm {
1065c2aa98e2SPeter Wemm 	/*
1066c2aa98e2SPeter Wemm 	 * Log the message to stderr.
1067c2aa98e2SPeter Wemm 	 *
1068c2aa98e2SPeter Wemm 	 * Don't use LOG_PERROR as an openlog() flag to do this,
1069c2aa98e2SPeter Wemm 	 * it's not portable enough.
1070c2aa98e2SPeter Wemm 	 */
1071c2aa98e2SPeter Wemm 	if (eval != EX_USAGE)
1072c2aa98e2SPeter Wemm 		(void)fprintf(stderr, "mail.local: ");
1073c2aa98e2SPeter Wemm 	(void)vfprintf(stderr, fmt, ap);
1074c2aa98e2SPeter Wemm 	(void)fprintf(stderr, "\n");
1075c2aa98e2SPeter Wemm 
1076c2aa98e2SPeter Wemm #if USE_VSYSLOG
1077c2aa98e2SPeter Wemm 	/* Log the message to syslog. */
1078c2aa98e2SPeter Wemm 	vsyslog(LOG_ERR, fmt, ap);
1079c2aa98e2SPeter Wemm #else
1080c2aa98e2SPeter Wemm 	{
1081c2aa98e2SPeter Wemm 		char fmtbuf[10240];
1082c2aa98e2SPeter Wemm 
1083c2aa98e2SPeter Wemm 		(void) vsnprintf(fmtbuf, sizeof fmtbuf, fmt, ap);
1084c2aa98e2SPeter Wemm 		syslog(LOG_ERR, "%s", fmtbuf);
1085c2aa98e2SPeter Wemm 	}
1086c2aa98e2SPeter Wemm #endif
1087c2aa98e2SPeter Wemm }
1088c2aa98e2SPeter Wemm 
1089c2aa98e2SPeter Wemm /*
1090c2aa98e2SPeter Wemm  * e_to_sys --
1091c2aa98e2SPeter Wemm  *	Guess which errno's are temporary.  Gag me.
1092c2aa98e2SPeter Wemm  */
1093c2aa98e2SPeter Wemm void
1094c2aa98e2SPeter Wemm e_to_sys(num)
1095c2aa98e2SPeter Wemm 	int num;
1096c2aa98e2SPeter Wemm {
1097c2aa98e2SPeter Wemm 	/* Temporary failures override hard errors. */
1098c2aa98e2SPeter Wemm 	if (eval == EX_TEMPFAIL)
1099c2aa98e2SPeter Wemm 		return;
1100c2aa98e2SPeter Wemm 
1101c2aa98e2SPeter Wemm 	switch(num) {		/* Hopefully temporary errors. */
1102c2aa98e2SPeter Wemm #ifdef EAGAIN
1103c2aa98e2SPeter Wemm 	case EAGAIN:		/* Resource temporarily unavailable */
1104c2aa98e2SPeter Wemm #endif
1105c2aa98e2SPeter Wemm #ifdef EDQUOT
1106c2aa98e2SPeter Wemm 	case EDQUOT:		/* Disc quota exceeded */
1107c2aa98e2SPeter Wemm #endif
1108c2aa98e2SPeter Wemm #ifdef EBUSY
1109c2aa98e2SPeter Wemm 	case EBUSY:		/* Device busy */
1110c2aa98e2SPeter Wemm #endif
1111c2aa98e2SPeter Wemm #ifdef EPROCLIM
1112c2aa98e2SPeter Wemm 	case EPROCLIM:		/* Too many processes */
1113c2aa98e2SPeter Wemm #endif
1114c2aa98e2SPeter Wemm #ifdef EUSERS
1115c2aa98e2SPeter Wemm 	case EUSERS:		/* Too many users */
1116c2aa98e2SPeter Wemm #endif
1117c2aa98e2SPeter Wemm #ifdef ECONNABORTED
1118c2aa98e2SPeter Wemm 	case ECONNABORTED:	/* Software caused connection abort */
1119c2aa98e2SPeter Wemm #endif
1120c2aa98e2SPeter Wemm #ifdef ECONNREFUSED
1121c2aa98e2SPeter Wemm 	case ECONNREFUSED:	/* Connection refused */
1122c2aa98e2SPeter Wemm #endif
1123c2aa98e2SPeter Wemm #ifdef ECONNRESET
1124c2aa98e2SPeter Wemm 	case ECONNRESET:	/* Connection reset by peer */
1125c2aa98e2SPeter Wemm #endif
1126c2aa98e2SPeter Wemm #ifdef EDEADLK
1127c2aa98e2SPeter Wemm 	case EDEADLK:		/* Resource deadlock avoided */
1128c2aa98e2SPeter Wemm #endif
1129c2aa98e2SPeter Wemm #ifdef EFBIG
1130c2aa98e2SPeter Wemm 	case EFBIG:		/* File too large */
1131c2aa98e2SPeter Wemm #endif
1132c2aa98e2SPeter Wemm #ifdef EHOSTDOWN
1133c2aa98e2SPeter Wemm 	case EHOSTDOWN:		/* Host is down */
1134c2aa98e2SPeter Wemm #endif
1135c2aa98e2SPeter Wemm #ifdef EHOSTUNREACH
1136c2aa98e2SPeter Wemm 	case EHOSTUNREACH:	/* No route to host */
1137c2aa98e2SPeter Wemm #endif
1138c2aa98e2SPeter Wemm #ifdef EMFILE
1139c2aa98e2SPeter Wemm 	case EMFILE:		/* Too many open files */
1140c2aa98e2SPeter Wemm #endif
1141c2aa98e2SPeter Wemm #ifdef ENETDOWN
1142c2aa98e2SPeter Wemm 	case ENETDOWN:		/* Network is down */
1143c2aa98e2SPeter Wemm #endif
1144c2aa98e2SPeter Wemm #ifdef ENETRESET
1145c2aa98e2SPeter Wemm 	case ENETRESET:		/* Network dropped connection on reset */
1146c2aa98e2SPeter Wemm #endif
1147c2aa98e2SPeter Wemm #ifdef ENETUNREACH
1148c2aa98e2SPeter Wemm 	case ENETUNREACH:	/* Network is unreachable */
1149c2aa98e2SPeter Wemm #endif
1150c2aa98e2SPeter Wemm #ifdef ENFILE
1151c2aa98e2SPeter Wemm 	case ENFILE:		/* Too many open files in system */
1152c2aa98e2SPeter Wemm #endif
1153c2aa98e2SPeter Wemm #ifdef ENOBUFS
1154c2aa98e2SPeter Wemm 	case ENOBUFS:		/* No buffer space available */
1155c2aa98e2SPeter Wemm #endif
1156c2aa98e2SPeter Wemm #ifdef ENOMEM
1157c2aa98e2SPeter Wemm 	case ENOMEM:		/* Cannot allocate memory */
1158c2aa98e2SPeter Wemm #endif
1159c2aa98e2SPeter Wemm #ifdef ENOSPC
1160c2aa98e2SPeter Wemm 	case ENOSPC:		/* No space left on device */
1161c2aa98e2SPeter Wemm #endif
1162c2aa98e2SPeter Wemm #ifdef EROFS
1163c2aa98e2SPeter Wemm 	case EROFS:		/* Read-only file system */
1164c2aa98e2SPeter Wemm #endif
1165c2aa98e2SPeter Wemm #ifdef ESTALE
1166c2aa98e2SPeter Wemm 	case ESTALE:		/* Stale NFS file handle */
1167c2aa98e2SPeter Wemm #endif
1168c2aa98e2SPeter Wemm #ifdef ETIMEDOUT
1169c2aa98e2SPeter Wemm 	case ETIMEDOUT:		/* Connection timed out */
1170c2aa98e2SPeter Wemm #endif
1171c2aa98e2SPeter Wemm #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
1172c2aa98e2SPeter Wemm 	case EWOULDBLOCK:	/* Operation would block. */
1173c2aa98e2SPeter Wemm #endif
1174c2aa98e2SPeter Wemm 		eval = EX_TEMPFAIL;
1175c2aa98e2SPeter Wemm 		break;
1176c2aa98e2SPeter Wemm 	default:
1177c2aa98e2SPeter Wemm 		eval = EX_UNAVAILABLE;
1178c2aa98e2SPeter Wemm 		break;
1179c2aa98e2SPeter Wemm 	}
1180c2aa98e2SPeter Wemm }
1181c2aa98e2SPeter Wemm 
1182c2aa98e2SPeter Wemm #if !HASSTRERROR
1183c2aa98e2SPeter Wemm 
1184c2aa98e2SPeter Wemm char *
1185c2aa98e2SPeter Wemm strerror(eno)
1186c2aa98e2SPeter Wemm 	int eno;
1187c2aa98e2SPeter Wemm {
1188c2aa98e2SPeter Wemm 	extern int sys_nerr;
1189c2aa98e2SPeter Wemm 	extern char *sys_errlist[];
1190c2aa98e2SPeter Wemm 	static char ebuf[60];
1191c2aa98e2SPeter Wemm 
1192c2aa98e2SPeter Wemm 	if (eno >= 0 && eno < sys_nerr)
1193c2aa98e2SPeter Wemm 		return sys_errlist[eno];
1194c2aa98e2SPeter Wemm 	(void) sprintf(ebuf, "Error %d", eno);
1195c2aa98e2SPeter Wemm 	return ebuf;
1196c2aa98e2SPeter Wemm }
1197c2aa98e2SPeter Wemm 
1198c2aa98e2SPeter Wemm #endif /* !HASSTRERROR */
1199c2aa98e2SPeter Wemm 
1200c2aa98e2SPeter Wemm #if defined(ultrix) || defined(_CRAY)
1201c2aa98e2SPeter Wemm 
1202c2aa98e2SPeter Wemm /*
1203c2aa98e2SPeter Wemm  * Copyright (c) 1987, 1993
1204c2aa98e2SPeter Wemm  *	The Regents of the University of California.  All rights reserved.
1205c2aa98e2SPeter Wemm  *
1206c2aa98e2SPeter Wemm  * Redistribution and use in source and binary forms, with or without
1207c2aa98e2SPeter Wemm  * modification, are permitted provided that the following conditions
1208c2aa98e2SPeter Wemm  * are met:
1209c2aa98e2SPeter Wemm  * 1. Redistributions of source code must retain the above copyright
1210c2aa98e2SPeter Wemm  *    notice, this list of conditions and the following disclaimer.
1211c2aa98e2SPeter Wemm  * 2. Redistributions in binary form must reproduce the above copyright
1212c2aa98e2SPeter Wemm  *    notice, this list of conditions and the following disclaimer in the
1213c2aa98e2SPeter Wemm  *    documentation and/or other materials provided with the distribution.
1214c2aa98e2SPeter Wemm  * 3. All advertising materials mentioning features or use of this software
1215c2aa98e2SPeter Wemm  *    must display the following acknowledgement:
1216c2aa98e2SPeter Wemm  *	This product includes software developed by the University of
1217c2aa98e2SPeter Wemm  *	California, Berkeley and its contributors.
1218c2aa98e2SPeter Wemm  * 4. Neither the name of the University nor the names of its contributors
1219c2aa98e2SPeter Wemm  *    may be used to endorse or promote products derived from this software
1220c2aa98e2SPeter Wemm  *    without specific prior written permission.
1221c2aa98e2SPeter Wemm  *
1222c2aa98e2SPeter Wemm  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1223c2aa98e2SPeter Wemm  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1224c2aa98e2SPeter Wemm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1225c2aa98e2SPeter Wemm  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1226c2aa98e2SPeter Wemm  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1227c2aa98e2SPeter Wemm  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1228c2aa98e2SPeter Wemm  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1229c2aa98e2SPeter Wemm  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1230c2aa98e2SPeter Wemm  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1231c2aa98e2SPeter Wemm  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1232c2aa98e2SPeter Wemm  * SUCH DAMAGE.
1233c2aa98e2SPeter Wemm  */
1234c2aa98e2SPeter Wemm 
1235c2aa98e2SPeter Wemm #if defined(LIBC_SCCS) && !defined(lint)
1236c2aa98e2SPeter Wemm static char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
1237c2aa98e2SPeter Wemm #endif /* LIBC_SCCS and not lint */
1238c2aa98e2SPeter Wemm 
1239c2aa98e2SPeter Wemm #include <sys/types.h>
1240c2aa98e2SPeter Wemm #include <sys/stat.h>
1241c2aa98e2SPeter Wemm #include <fcntl.h>
1242c2aa98e2SPeter Wemm #include <errno.h>
1243c2aa98e2SPeter Wemm #include <stdio.h>
1244c2aa98e2SPeter Wemm #include <ctype.h>
1245c2aa98e2SPeter Wemm 
1246c2aa98e2SPeter Wemm static int _gettemp();
1247c2aa98e2SPeter Wemm 
1248c2aa98e2SPeter Wemm mkstemp(path)
1249c2aa98e2SPeter Wemm 	char *path;
1250c2aa98e2SPeter Wemm {
1251c2aa98e2SPeter Wemm 	int fd;
1252c2aa98e2SPeter Wemm 
1253c2aa98e2SPeter Wemm 	return (_gettemp(path, &fd) ? fd : -1);
1254c2aa98e2SPeter Wemm }
1255c2aa98e2SPeter Wemm 
1256c2aa98e2SPeter Wemm /*
1257c2aa98e2SPeter Wemm char *
1258c2aa98e2SPeter Wemm mktemp(path)
1259c2aa98e2SPeter Wemm 	char *path;
1260c2aa98e2SPeter Wemm {
1261c2aa98e2SPeter Wemm 	return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
1262c2aa98e2SPeter Wemm }
1263c2aa98e2SPeter Wemm */
1264c2aa98e2SPeter Wemm 
1265c2aa98e2SPeter Wemm static
1266c2aa98e2SPeter Wemm _gettemp(path, doopen)
1267c2aa98e2SPeter Wemm 	char *path;
1268c2aa98e2SPeter Wemm 	register int *doopen;
1269c2aa98e2SPeter Wemm {
1270c2aa98e2SPeter Wemm 	extern int errno;
1271c2aa98e2SPeter Wemm 	register char *start, *trv;
1272c2aa98e2SPeter Wemm 	struct stat sbuf;
1273c2aa98e2SPeter Wemm 	u_int pid;
1274c2aa98e2SPeter Wemm 
1275c2aa98e2SPeter Wemm 	pid = getpid();
1276c2aa98e2SPeter Wemm 	for (trv = path; *trv; ++trv);		/* extra X's get set to 0's */
1277c2aa98e2SPeter Wemm 	while (*--trv == 'X') {
1278c2aa98e2SPeter Wemm 		*trv = (pid % 10) + '0';
1279c2aa98e2SPeter Wemm 		pid /= 10;
1280c2aa98e2SPeter Wemm 	}
1281c2aa98e2SPeter Wemm 
1282c2aa98e2SPeter Wemm 	/*
1283c2aa98e2SPeter Wemm 	 * check the target directory; if you have six X's and it
1284c2aa98e2SPeter Wemm 	 * doesn't exist this runs for a *very* long time.
1285c2aa98e2SPeter Wemm 	 */
1286c2aa98e2SPeter Wemm 	for (start = trv + 1;; --trv) {
1287c2aa98e2SPeter Wemm 		if (trv <= path)
1288c2aa98e2SPeter Wemm 			break;
1289c2aa98e2SPeter Wemm 		if (*trv == '/') {
1290c2aa98e2SPeter Wemm 			*trv = '\0';
1291c2aa98e2SPeter Wemm 			if (stat(path, &sbuf) < 0)
1292c2aa98e2SPeter Wemm 				return(0);
1293c2aa98e2SPeter Wemm 			if (!S_ISDIR(sbuf.st_mode)) {
1294c2aa98e2SPeter Wemm 				errno = ENOTDIR;
1295c2aa98e2SPeter Wemm 				return(0);
1296c2aa98e2SPeter Wemm 			}
1297c2aa98e2SPeter Wemm 			*trv = '/';
1298c2aa98e2SPeter Wemm 			break;
1299c2aa98e2SPeter Wemm 		}
1300c2aa98e2SPeter Wemm 	}
1301c2aa98e2SPeter Wemm 
1302c2aa98e2SPeter Wemm 	for (;;) {
1303c2aa98e2SPeter Wemm 		if (doopen) {
1304c2aa98e2SPeter Wemm 			if ((*doopen =
1305c2aa98e2SPeter Wemm 			    open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
1306c2aa98e2SPeter Wemm 				return(1);
1307c2aa98e2SPeter Wemm 			if (errno != EEXIST)
1308c2aa98e2SPeter Wemm 				return(0);
1309c2aa98e2SPeter Wemm 		}
1310c2aa98e2SPeter Wemm 		else if (stat(path, &sbuf) < 0)
1311c2aa98e2SPeter Wemm 			return(errno == ENOENT ? 1 : 0);
1312c2aa98e2SPeter Wemm 
1313c2aa98e2SPeter Wemm 		/* tricky little algorithm for backward compatibility */
1314c2aa98e2SPeter Wemm 		for (trv = start;;) {
1315c2aa98e2SPeter Wemm 			if (!*trv)
1316c2aa98e2SPeter Wemm 				return(0);
1317c2aa98e2SPeter Wemm 			if (*trv == 'z')
1318c2aa98e2SPeter Wemm 				*trv++ = 'a';
1319c2aa98e2SPeter Wemm 			else {
1320c2aa98e2SPeter Wemm 				if (isascii(*trv) && isdigit(*trv))
1321c2aa98e2SPeter Wemm 					*trv = 'a';
1322c2aa98e2SPeter Wemm 				else
1323c2aa98e2SPeter Wemm 					++*trv;
1324c2aa98e2SPeter Wemm 				break;
1325c2aa98e2SPeter Wemm 			}
1326c2aa98e2SPeter Wemm 		}
1327c2aa98e2SPeter Wemm 	}
1328c2aa98e2SPeter Wemm 	/*NOTREACHED*/
1329c2aa98e2SPeter Wemm }
1330c2aa98e2SPeter Wemm 
1331c2aa98e2SPeter Wemm #endif /* ultrix */
1332