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