xref: /freebsd/contrib/sendmail/mail.local/mail.local.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
176b7bf71SPeter Wemm /*
2f9218d3dSGregory Neil Shapiro  * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
33299c2f1SGregory Neil Shapiro  *	All rights reserved.
4c2aa98e2SPeter Wemm  * Copyright (c) 1990, 1993, 1994
5c2aa98e2SPeter Wemm  *	The Regents of the University of California.  All rights reserved.
6c2aa98e2SPeter Wemm  *
7c2aa98e2SPeter Wemm  * By using this file, you agree to the terms and conditions set
8c2aa98e2SPeter Wemm  * forth in the LICENSE file which can be found at the top level of
9c2aa98e2SPeter Wemm  * the sendmail distribution.
10c2aa98e2SPeter Wemm  *
113edfa581SGregory Neil Shapiro  * $FreeBSD$
123edfa581SGregory Neil Shapiro  *
13c2aa98e2SPeter Wemm  */
14c2aa98e2SPeter Wemm 
1512ed1c7cSGregory Neil Shapiro #include <sm/gen.h>
1612ed1c7cSGregory Neil Shapiro 
1712ed1c7cSGregory Neil Shapiro SM_IDSTR(copyright,
18b4662009SGregory Neil Shapiro "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
193299c2f1SGregory Neil Shapiro 	All rights reserved.\n\
203299c2f1SGregory Neil Shapiro      Copyright (c) 1990, 1993, 1994\n\
2112ed1c7cSGregory Neil Shapiro 	The Regents of the University of California.  All rights reserved.\n")
22c2aa98e2SPeter Wemm 
237660b554SGregory Neil Shapiro SM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.239.2.11 2003/09/01 01:49:46 gshapiro Exp $")
2412ed1c7cSGregory Neil Shapiro 
2512ed1c7cSGregory Neil Shapiro #include <stdlib.h>
2612ed1c7cSGregory Neil Shapiro #include <sm/errstring.h>
2712ed1c7cSGregory Neil Shapiro #include <sm/io.h>
2812ed1c7cSGregory Neil Shapiro #include <sm/limits.h>
2912ed1c7cSGregory Neil Shapiro # include <unistd.h>
3012ed1c7cSGregory Neil Shapiro # ifdef EX_OK
3112ed1c7cSGregory Neil Shapiro #  undef EX_OK		/* unistd.h may have another use for this */
3212ed1c7cSGregory Neil Shapiro # endif /* EX_OK */
337660b554SGregory Neil Shapiro # define LOCKFILE_PMODE 0
3412ed1c7cSGregory Neil Shapiro #include <sm/mbdb.h>
3512ed1c7cSGregory Neil Shapiro #include <sm/sysexits.h>
363299c2f1SGregory Neil Shapiro 
37c2aa98e2SPeter Wemm /*
383299c2f1SGregory Neil Shapiro **  This is not intended to work on System V derived systems
393299c2f1SGregory Neil Shapiro **  such as Solaris or HP-UX, since they use a totally different
4012ed1c7cSGregory Neil Shapiro **  approach to mailboxes (essentially, they have a set-group-ID program
4112ed1c7cSGregory Neil Shapiro **  rather than set-user-ID, and they rely on the ability to "give away"
423299c2f1SGregory Neil Shapiro **  files to do their work).  IT IS NOT A BUG that this doesn't
433299c2f1SGregory Neil Shapiro **  work on such architectures.
44c2aa98e2SPeter Wemm */
45c2aa98e2SPeter Wemm 
463299c2f1SGregory Neil Shapiro 
4712ed1c7cSGregory Neil Shapiro #include <stdio.h>
4812ed1c7cSGregory Neil Shapiro #include <errno.h>
4912ed1c7cSGregory Neil Shapiro #include <fcntl.h>
5012ed1c7cSGregory Neil Shapiro #include <sys/types.h>
5112ed1c7cSGregory Neil Shapiro #include <sys/stat.h>
5212ed1c7cSGregory Neil Shapiro #include <time.h>
5312ed1c7cSGregory Neil Shapiro #include <stdlib.h>
5412ed1c7cSGregory Neil Shapiro # include <sys/socket.h>
5512ed1c7cSGregory Neil Shapiro # include <sys/file.h>
5612ed1c7cSGregory Neil Shapiro # include <netinet/in.h>
5712ed1c7cSGregory Neil Shapiro # include <arpa/nameser.h>
5812ed1c7cSGregory Neil Shapiro # include <netdb.h>
5912ed1c7cSGregory Neil Shapiro # include <pwd.h>
6012ed1c7cSGregory Neil Shapiro 
6112ed1c7cSGregory Neil Shapiro #include <sm/string.h>
6212ed1c7cSGregory Neil Shapiro #include <syslog.h>
6312ed1c7cSGregory Neil Shapiro #include <ctype.h>
6412ed1c7cSGregory Neil Shapiro 
6512ed1c7cSGregory Neil Shapiro #include <sm/conf.h>
6612ed1c7cSGregory Neil Shapiro #include <sendmail/pathnames.h>
6712ed1c7cSGregory Neil Shapiro 
6812ed1c7cSGregory Neil Shapiro 
693299c2f1SGregory Neil Shapiro #ifndef LOCKTO_RM
703299c2f1SGregory Neil Shapiro # define LOCKTO_RM	300	/* timeout for stale lockfile removal */
71d995d2baSGregory Neil Shapiro #endif /* ! LOCKTO_RM */
723299c2f1SGregory Neil Shapiro #ifndef LOCKTO_GLOB
733299c2f1SGregory Neil Shapiro # define LOCKTO_GLOB	400	/* global timeout for lockfile creation */
74d995d2baSGregory Neil Shapiro #endif /* ! LOCKTO_GLOB */
753299c2f1SGregory Neil Shapiro 
763299c2f1SGregory Neil Shapiro /* define a realloc() which works for NULL pointers */
773299c2f1SGregory Neil Shapiro #define REALLOC(ptr, size)	(((ptr) == NULL) ? malloc(size) : realloc(ptr, size))
78c2aa98e2SPeter Wemm 
79c2aa98e2SPeter Wemm /*
8012ed1c7cSGregory Neil Shapiro **  If you don't have flock, you could try using lockf instead.
81c2aa98e2SPeter Wemm */
82c2aa98e2SPeter Wemm 
8312ed1c7cSGregory Neil Shapiro #ifdef LDA_USE_LOCKF
84c2aa98e2SPeter Wemm # define flock(a, b)	lockf(a, b, 0)
853299c2f1SGregory Neil Shapiro # ifdef LOCK_EX
863299c2f1SGregory Neil Shapiro #  undef LOCK_EX
873299c2f1SGregory Neil Shapiro # endif /* LOCK_EX */
88c2aa98e2SPeter Wemm # define LOCK_EX	F_LOCK
8912ed1c7cSGregory Neil Shapiro #endif /* LDA_USE_LOCKF */
90c2aa98e2SPeter Wemm 
91c2aa98e2SPeter Wemm #ifndef LOCK_EX
92c2aa98e2SPeter Wemm # include <sys/file.h>
933299c2f1SGregory Neil Shapiro #endif /* ! LOCK_EX */
94c2aa98e2SPeter Wemm 
95c2aa98e2SPeter Wemm /*
963299c2f1SGregory Neil Shapiro **  If you don't have setreuid, and you have saved uids, and you have
973299c2f1SGregory Neil Shapiro **  a seteuid() call that doesn't try to emulate using setuid(), then
9812ed1c7cSGregory Neil Shapiro **  you can try defining LDA_USE_SETEUID.
99c2aa98e2SPeter Wemm */
100b4662009SGregory Neil Shapiro 
10112ed1c7cSGregory Neil Shapiro #ifdef LDA_USE_SETEUID
102c2aa98e2SPeter Wemm # define setreuid(r, e)		seteuid(e)
10312ed1c7cSGregory Neil Shapiro #endif /* LDA_USE_SETEUID */
104c2aa98e2SPeter Wemm 
10512ed1c7cSGregory Neil Shapiro #ifdef LDA_CONTENTLENGTH
10612ed1c7cSGregory Neil Shapiro # define CONTENTLENGTH	1
10712ed1c7cSGregory Neil Shapiro #endif /* LDA_CONTENTLENGTH */
108d995d2baSGregory Neil Shapiro 
1093299c2f1SGregory Neil Shapiro #ifndef INADDRSZ
1103299c2f1SGregory Neil Shapiro # define INADDRSZ	4		/* size of an IPv4 address in bytes */
1113299c2f1SGregory Neil Shapiro #endif /* ! INADDRSZ */
112c2aa98e2SPeter Wemm 
11312ed1c7cSGregory Neil Shapiro #ifdef MAILLOCK
11412ed1c7cSGregory Neil Shapiro # include <maillock.h>
11512ed1c7cSGregory Neil Shapiro #endif /* MAILLOCK */
11612ed1c7cSGregory Neil Shapiro 
11776b7bf71SPeter Wemm #ifndef MAILER_DAEMON
11876b7bf71SPeter Wemm # define MAILER_DAEMON	"MAILER-DAEMON"
1193299c2f1SGregory Neil Shapiro #endif /* ! MAILER_DAEMON */
12076b7bf71SPeter Wemm 
1213299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH
1223299c2f1SGregory Neil Shapiro char	ContentHdr[40] = "Content-Length: ";
1233299c2f1SGregory Neil Shapiro off_t	HeaderLength;
1243299c2f1SGregory Neil Shapiro off_t	BodyLength;
1253299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */
126c2aa98e2SPeter Wemm 
12712ed1c7cSGregory Neil Shapiro bool	EightBitMime = true;		/* advertise 8BITMIME in LMTP */
128b4662009SGregory Neil Shapiro char	ErrBuf[10240];			/* error buffer */
1293299c2f1SGregory Neil Shapiro int	ExitVal = EX_OK;		/* sysexits.h error value. */
13012ed1c7cSGregory Neil Shapiro bool	nobiff = false;
13112ed1c7cSGregory Neil Shapiro bool	nofsync = false;
13212ed1c7cSGregory Neil Shapiro bool	HoldErrs = false;		/* Hold errors in ErrBuf */
13312ed1c7cSGregory Neil Shapiro bool	LMTPMode = false;
13412ed1c7cSGregory Neil Shapiro bool	BounceQuota = false;		/* permanent error when over quota */
13512ed1c7cSGregory Neil Shapiro char	*HomeMailFile = NULL;		/* store mail in homedir */
1363299c2f1SGregory Neil Shapiro 
137b4662009SGregory Neil Shapiro void	deliver __P((int, char *));
1383299c2f1SGregory Neil Shapiro int	e_to_sys __P((int));
139c2aa98e2SPeter Wemm void	notifybiff __P((char *));
14012ed1c7cSGregory Neil Shapiro int	store __P((char *, bool *));
141c2aa98e2SPeter Wemm void	usage __P((void));
1423299c2f1SGregory Neil Shapiro int	lockmbox __P((char *));
143c2aa98e2SPeter Wemm void	unlockmbox __P((void));
144c2aa98e2SPeter Wemm void	mailerr __P((const char *, const char *, ...));
145b4662009SGregory Neil Shapiro void	flush_error __P((void));
1463299c2f1SGregory Neil Shapiro 
147c2aa98e2SPeter Wemm 
148c2aa98e2SPeter Wemm int
149c2aa98e2SPeter Wemm main(argc, argv)
150c2aa98e2SPeter Wemm 	int argc;
151c2aa98e2SPeter Wemm 	char *argv[];
152c2aa98e2SPeter Wemm {
153c2aa98e2SPeter Wemm 	struct passwd *pw;
1543299c2f1SGregory Neil Shapiro 	int ch, fd;
155c2aa98e2SPeter Wemm 	uid_t uid;
156c2aa98e2SPeter Wemm 	char *from;
15712ed1c7cSGregory Neil Shapiro 	char *mbdbname = "pw";
15812ed1c7cSGregory Neil Shapiro 	int err;
159c2aa98e2SPeter Wemm 	extern char *optarg;
160c2aa98e2SPeter Wemm 	extern int optind;
1613299c2f1SGregory Neil Shapiro 
162c2aa98e2SPeter Wemm 
163c2aa98e2SPeter Wemm 	/* make sure we have some open file descriptors */
164c2aa98e2SPeter Wemm 	for (fd = 10; fd < 30; fd++)
165c2aa98e2SPeter Wemm 		(void) close(fd);
166c2aa98e2SPeter Wemm 
167c2aa98e2SPeter Wemm 	/* use a reasonable umask */
168c2aa98e2SPeter Wemm 	(void) umask(0077);
169c2aa98e2SPeter Wemm 
170c2aa98e2SPeter Wemm # ifdef LOG_MAIL
171c2aa98e2SPeter Wemm 	openlog("mail.local", 0, LOG_MAIL);
1723299c2f1SGregory Neil Shapiro # else /* LOG_MAIL */
173c2aa98e2SPeter Wemm 	openlog("mail.local", 0);
1743299c2f1SGregory Neil Shapiro # endif /* LOG_MAIL */
175c2aa98e2SPeter Wemm 
176c2aa98e2SPeter Wemm 	from = NULL;
17712ed1c7cSGregory Neil Shapiro 	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:ls")) != -1)
1783299c2f1SGregory Neil Shapiro 	{
1793299c2f1SGregory Neil Shapiro 		switch(ch)
1803299c2f1SGregory Neil Shapiro 		{
1813299c2f1SGregory Neil Shapiro 		  case '7':		/* Do not advertise 8BITMIME */
18212ed1c7cSGregory Neil Shapiro 			EightBitMime = false;
183d615a192SPeter Wemm 			break;
1843299c2f1SGregory Neil Shapiro 
1853299c2f1SGregory Neil Shapiro 		  case 'B':
18639e37e72SGregory Neil Shapiro 			nobiff = true;
1873299c2f1SGregory Neil Shapiro 			break;
1883299c2f1SGregory Neil Shapiro 
1893299c2f1SGregory Neil Shapiro 		  case 'b':		/* bounce mail when over quota. */
19012ed1c7cSGregory Neil Shapiro 			BounceQuota = true;
1913299c2f1SGregory Neil Shapiro 			break;
1923299c2f1SGregory Neil Shapiro 
193c2aa98e2SPeter Wemm 		  case 'd':		/* Backward compatible. */
194c2aa98e2SPeter Wemm 			break;
1953299c2f1SGregory Neil Shapiro 
19612ed1c7cSGregory Neil Shapiro 		  case 'D':		/* mailbox database type */
19712ed1c7cSGregory Neil Shapiro 			mbdbname = optarg;
19812ed1c7cSGregory Neil Shapiro 			break;
19912ed1c7cSGregory Neil Shapiro 
200c2aa98e2SPeter Wemm 		  case 'f':
201c2aa98e2SPeter Wemm 		  case 'r':		/* Backward compatible. */
2023299c2f1SGregory Neil Shapiro 			if (from != NULL)
2033299c2f1SGregory Neil Shapiro 			{
204b4662009SGregory Neil Shapiro 				mailerr(NULL, "Multiple -f options");
205c2aa98e2SPeter Wemm 				usage();
206c2aa98e2SPeter Wemm 			}
207c2aa98e2SPeter Wemm 			from = optarg;
208c2aa98e2SPeter Wemm 			break;
2093299c2f1SGregory Neil Shapiro 
21012ed1c7cSGregory Neil Shapiro 		  case 'h':
21112ed1c7cSGregory Neil Shapiro 			if (optarg != NULL || *optarg != '\0')
21212ed1c7cSGregory Neil Shapiro 				HomeMailFile = optarg;
21312ed1c7cSGregory Neil Shapiro 			else
21412ed1c7cSGregory Neil Shapiro 			{
21512ed1c7cSGregory Neil Shapiro 				mailerr(NULL, "-h: missing filename");
21612ed1c7cSGregory Neil Shapiro 				usage();
21712ed1c7cSGregory Neil Shapiro 			}
21812ed1c7cSGregory Neil Shapiro 			break;
21912ed1c7cSGregory Neil Shapiro 
220c2aa98e2SPeter Wemm 		  case 'l':
22112ed1c7cSGregory Neil Shapiro 			LMTPMode = true;
222c2aa98e2SPeter Wemm 			break;
2233299c2f1SGregory Neil Shapiro 
224d615a192SPeter Wemm 		  case 's':
22505b73c60SPeter Wemm 			nofsync++;
226d615a192SPeter Wemm 			break;
2273299c2f1SGregory Neil Shapiro 
228c2aa98e2SPeter Wemm 		  case '?':
229c2aa98e2SPeter Wemm 		  default:
230c2aa98e2SPeter Wemm 			usage();
231c2aa98e2SPeter Wemm 		}
2323299c2f1SGregory Neil Shapiro 	}
233c2aa98e2SPeter Wemm 	argc -= optind;
234c2aa98e2SPeter Wemm 	argv += optind;
235c2aa98e2SPeter Wemm 
2363299c2f1SGregory Neil Shapiro 	/* initialize biff structures */
2373299c2f1SGregory Neil Shapiro 	if (!nobiff)
2383299c2f1SGregory Neil Shapiro 		notifybiff(NULL);
239c2aa98e2SPeter Wemm 
24012ed1c7cSGregory Neil Shapiro 	err = sm_mbdb_initialize(mbdbname);
24112ed1c7cSGregory Neil Shapiro 	if (err != EX_OK)
24212ed1c7cSGregory Neil Shapiro 	{
24312ed1c7cSGregory Neil Shapiro 		char *errcode = "521";
24412ed1c7cSGregory Neil Shapiro 
24512ed1c7cSGregory Neil Shapiro 		if (err == EX_TEMPFAIL)
24612ed1c7cSGregory Neil Shapiro 			errcode = "421";
24712ed1c7cSGregory Neil Shapiro 
24812ed1c7cSGregory Neil Shapiro 		mailerr(errcode, "Can not open mailbox database %s: %s",
24912ed1c7cSGregory Neil Shapiro 			mbdbname, sm_strexit(err));
25012ed1c7cSGregory Neil Shapiro 		exit(err);
25112ed1c7cSGregory Neil Shapiro 	}
25212ed1c7cSGregory Neil Shapiro 
2533299c2f1SGregory Neil Shapiro 	if (LMTPMode)
254b4662009SGregory Neil Shapiro 	{
255b4662009SGregory Neil Shapiro 		extern void dolmtp __P((void));
2563299c2f1SGregory Neil Shapiro 
257b4662009SGregory Neil Shapiro 		if (argc > 0)
258b4662009SGregory Neil Shapiro 		{
259b4662009SGregory Neil Shapiro 			mailerr("421", "Users should not be specified in command line if LMTP required");
260b4662009SGregory Neil Shapiro 			exit(EX_TEMPFAIL);
261b4662009SGregory Neil Shapiro 		}
262b4662009SGregory Neil Shapiro 
263b4662009SGregory Neil Shapiro 		dolmtp();
264b4662009SGregory Neil Shapiro 		/* NOTREACHED */
265b4662009SGregory Neil Shapiro 		exit(EX_OK);
266b4662009SGregory Neil Shapiro 	}
267b4662009SGregory Neil Shapiro 
268b4662009SGregory Neil Shapiro 	/* Non-LMTP from here on out */
2693299c2f1SGregory Neil Shapiro 	if (*argv == '\0')
270c2aa98e2SPeter Wemm 		usage();
271c2aa98e2SPeter Wemm 
272c2aa98e2SPeter Wemm 	/*
2733299c2f1SGregory Neil Shapiro 	**  If from not specified, use the name from getlogin() if the
2743299c2f1SGregory Neil Shapiro 	**  uid matches, otherwise, use the name from the password file
2753299c2f1SGregory Neil Shapiro 	**  corresponding to the uid.
276c2aa98e2SPeter Wemm 	*/
277b4662009SGregory Neil Shapiro 
278c2aa98e2SPeter Wemm 	uid = getuid();
2793299c2f1SGregory Neil Shapiro 	if (from == NULL && ((from = getlogin()) == NULL ||
2803299c2f1SGregory Neil Shapiro 			     (pw = getpwnam(from)) == NULL ||
2813299c2f1SGregory Neil Shapiro 			     pw->pw_uid != uid))
2823299c2f1SGregory Neil Shapiro 		from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???";
283c2aa98e2SPeter Wemm 
284c2aa98e2SPeter Wemm 	/*
2853299c2f1SGregory Neil Shapiro 	**  There is no way to distinguish the error status of one delivery
2863299c2f1SGregory Neil Shapiro 	**  from the rest of the deliveries.  So, if we failed hard on one
2873299c2f1SGregory Neil Shapiro 	**  or more deliveries, but had no failures on any of the others, we
2883299c2f1SGregory Neil Shapiro 	**  return a hard failure.  If we failed temporarily on one or more
2893299c2f1SGregory Neil Shapiro 	**  deliveries, we return a temporary failure regardless of the other
2903299c2f1SGregory Neil Shapiro 	**  failures.  This results in the delivery being reattempted later
2913299c2f1SGregory Neil Shapiro 	**  at the expense of repeated failures and multiple deliveries.
292c2aa98e2SPeter Wemm 	*/
293b4662009SGregory Neil Shapiro 
29412ed1c7cSGregory Neil Shapiro 	HoldErrs = true;
29512ed1c7cSGregory Neil Shapiro 	fd = store(from, NULL);
29612ed1c7cSGregory Neil Shapiro 	HoldErrs = false;
297b4662009SGregory Neil Shapiro 	if (fd < 0)
298b4662009SGregory Neil Shapiro 	{
299b4662009SGregory Neil Shapiro 		flush_error();
300b4662009SGregory Neil Shapiro 		exit(ExitVal);
301b4662009SGregory Neil Shapiro 	}
302b4662009SGregory Neil Shapiro 	for (; *argv != NULL; ++argv)
303b4662009SGregory Neil Shapiro 		deliver(fd, *argv);
3043299c2f1SGregory Neil Shapiro 	exit(ExitVal);
3053299c2f1SGregory Neil Shapiro 	/* NOTREACHED */
3063299c2f1SGregory Neil Shapiro 	return ExitVal;
307c2aa98e2SPeter Wemm }
308c2aa98e2SPeter Wemm 
309c2aa98e2SPeter Wemm char *
3103299c2f1SGregory Neil Shapiro parseaddr(s, rcpt)
311c2aa98e2SPeter Wemm 	char *s;
3123299c2f1SGregory Neil Shapiro 	bool rcpt;
313c2aa98e2SPeter Wemm {
314c2aa98e2SPeter Wemm 	char *p;
3153299c2f1SGregory Neil Shapiro 	int l;
316c2aa98e2SPeter Wemm 
317c2aa98e2SPeter Wemm 	if (*s++ != '<')
318c2aa98e2SPeter Wemm 		return NULL;
319c2aa98e2SPeter Wemm 
320c2aa98e2SPeter Wemm 	p = s;
321c2aa98e2SPeter Wemm 
322c2aa98e2SPeter Wemm 	/* at-domain-list */
3233299c2f1SGregory Neil Shapiro 	while (*p == '@')
3243299c2f1SGregory Neil Shapiro 	{
325c2aa98e2SPeter Wemm 		p++;
3263299c2f1SGregory Neil Shapiro 		while (*p != ',' && *p != ':' && *p != '\0')
327c2aa98e2SPeter Wemm 			p++;
3283299c2f1SGregory Neil Shapiro 		if (*p == '\0')
329c2aa98e2SPeter Wemm 			return NULL;
3303299c2f1SGregory Neil Shapiro 
3313299c2f1SGregory Neil Shapiro 		/* Skip over , or : */
332c2aa98e2SPeter Wemm 		p++;
333c2aa98e2SPeter Wemm 	}
334c2aa98e2SPeter Wemm 
33576b7bf71SPeter Wemm 	s = p;
33676b7bf71SPeter Wemm 
337c2aa98e2SPeter Wemm 	/* local-part */
3383299c2f1SGregory Neil Shapiro 	while (*p != '\0' && *p != '@' && *p != '>')
3393299c2f1SGregory Neil Shapiro 	{
3403299c2f1SGregory Neil Shapiro 		if (*p == '\\')
3413299c2f1SGregory Neil Shapiro 		{
3423299c2f1SGregory Neil Shapiro 			if (*++p == '\0')
3433299c2f1SGregory Neil Shapiro 				return NULL;
3443299c2f1SGregory Neil Shapiro 		}
3453299c2f1SGregory Neil Shapiro 		else if (*p == '\"')
3463299c2f1SGregory Neil Shapiro 		{
347c2aa98e2SPeter Wemm 			p++;
3483299c2f1SGregory Neil Shapiro 			while (*p != '\0' && *p != '\"')
3493299c2f1SGregory Neil Shapiro 			{
3503299c2f1SGregory Neil Shapiro 				if (*p == '\\')
3513299c2f1SGregory Neil Shapiro 				{
3523299c2f1SGregory Neil Shapiro 					if (*++p == '\0')
353c2aa98e2SPeter Wemm 						return NULL;
354c2aa98e2SPeter Wemm 				}
355c2aa98e2SPeter Wemm 				p++;
356c2aa98e2SPeter Wemm 			}
3573299c2f1SGregory Neil Shapiro 			if (*p == '\0' || *(p + 1) == '\0')
358c2aa98e2SPeter Wemm 				return NULL;
359c2aa98e2SPeter Wemm 		}
3603299c2f1SGregory Neil Shapiro 		/* +detail ? */
3613299c2f1SGregory Neil Shapiro 		if (*p == '+' && rcpt)
3623299c2f1SGregory Neil Shapiro 			*p = '\0';
363c2aa98e2SPeter Wemm 		p++;
364c2aa98e2SPeter Wemm 	}
365c2aa98e2SPeter Wemm 
366c2aa98e2SPeter Wemm 	/* @domain */
3673299c2f1SGregory Neil Shapiro 	if (*p == '@')
36876b7bf71SPeter Wemm 	{
3693299c2f1SGregory Neil Shapiro 		if (rcpt)
3703299c2f1SGregory Neil Shapiro 			*p++ = '\0';
3713299c2f1SGregory Neil Shapiro 		while (*p != '\0' && *p != '>')
3723299c2f1SGregory Neil Shapiro 			p++;
37376b7bf71SPeter Wemm 	}
374c2aa98e2SPeter Wemm 
3753299c2f1SGregory Neil Shapiro 	if (*p != '>')
3763299c2f1SGregory Neil Shapiro 		return NULL;
3773299c2f1SGregory Neil Shapiro 	else
3783299c2f1SGregory Neil Shapiro 		*p = '\0';
3793299c2f1SGregory Neil Shapiro 	p++;
3803299c2f1SGregory Neil Shapiro 
3813299c2f1SGregory Neil Shapiro 	if (*p != '\0' && *p != ' ')
3823299c2f1SGregory Neil Shapiro 		return NULL;
3833299c2f1SGregory Neil Shapiro 
3843299c2f1SGregory Neil Shapiro 	if (*s == '\0')
3853299c2f1SGregory Neil Shapiro 		s = MAILER_DAEMON;
3863299c2f1SGregory Neil Shapiro 
3873299c2f1SGregory Neil Shapiro 	l = strlen(s) + 1;
38812ed1c7cSGregory Neil Shapiro 	if (l < 0)
38912ed1c7cSGregory Neil Shapiro 		return NULL;
3903299c2f1SGregory Neil Shapiro 	p = malloc(l);
3913299c2f1SGregory Neil Shapiro 	if (p == NULL)
3923299c2f1SGregory Neil Shapiro 	{
393b4662009SGregory Neil Shapiro 		mailerr("421 4.3.0", "Memory exhausted");
394c2aa98e2SPeter Wemm 		exit(EX_TEMPFAIL);
395c2aa98e2SPeter Wemm 	}
396c2aa98e2SPeter Wemm 
39712ed1c7cSGregory Neil Shapiro 	(void) sm_strlcpy(p, s, l);
398c2aa98e2SPeter Wemm 	return p;
399c2aa98e2SPeter Wemm }
400c2aa98e2SPeter Wemm 
401c2aa98e2SPeter Wemm char *
402c2aa98e2SPeter Wemm process_recipient(addr)
403c2aa98e2SPeter Wemm 	char *addr;
404c2aa98e2SPeter Wemm {
40512ed1c7cSGregory Neil Shapiro 	SM_MBDB_T user;
40612ed1c7cSGregory Neil Shapiro 
40712ed1c7cSGregory Neil Shapiro 	switch (sm_mbdb_lookup(addr, &user))
40812ed1c7cSGregory Neil Shapiro 	{
40912ed1c7cSGregory Neil Shapiro 	  case EX_OK:
410c2aa98e2SPeter Wemm 		return NULL;
41112ed1c7cSGregory Neil Shapiro 
41212ed1c7cSGregory Neil Shapiro 	  case EX_NOUSER:
41312ed1c7cSGregory Neil Shapiro 		return "550 5.1.1 User unknown";
41412ed1c7cSGregory Neil Shapiro 
41512ed1c7cSGregory Neil Shapiro 	  case EX_TEMPFAIL:
41612ed1c7cSGregory Neil Shapiro 		return "451 4.3.0 User database failure; retry later";
41712ed1c7cSGregory Neil Shapiro 
41812ed1c7cSGregory Neil Shapiro 	  default:
41912ed1c7cSGregory Neil Shapiro 		return "550 5.3.0 User database failure";
42012ed1c7cSGregory Neil Shapiro 	}
421c2aa98e2SPeter Wemm }
422c2aa98e2SPeter Wemm 
423c2aa98e2SPeter Wemm #define RCPT_GROW	30
424c2aa98e2SPeter Wemm 
425c2aa98e2SPeter Wemm void
426b4662009SGregory Neil Shapiro 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;
43212ed1c7cSGregory Neil Shapiro 	bool gotlhlo = false;
433c2aa98e2SPeter Wemm 	char *err;
434c2aa98e2SPeter Wemm 	int msgfd;
435c2aa98e2SPeter Wemm 	char *p;
436c2aa98e2SPeter Wemm 	int i;
4373299c2f1SGregory Neil Shapiro 	char myhostname[1024];
4383299c2f1SGregory Neil Shapiro 	char buf[4096];
439c2aa98e2SPeter Wemm 
440b4662009SGregory Neil Shapiro 	memset(myhostname, '\0', sizeof myhostname);
4413299c2f1SGregory Neil Shapiro 	(void) gethostname(myhostname, sizeof myhostname - 1);
442b4662009SGregory Neil Shapiro 	if (myhostname[0] == '\0')
44312ed1c7cSGregory Neil Shapiro 		sm_strlcpy(myhostname, "localhost", sizeof myhostname);
444c2aa98e2SPeter Wemm 
445c2aa98e2SPeter Wemm 	printf("220 %s LMTP ready\r\n", myhostname);
4463299c2f1SGregory Neil Shapiro 	for (;;)
4473299c2f1SGregory Neil Shapiro 	{
4483299c2f1SGregory Neil Shapiro 		(void) fflush(stdout);
4493299c2f1SGregory Neil Shapiro 		if (fgets(buf, sizeof(buf) - 1, stdin) == NULL)
450c2aa98e2SPeter Wemm 			exit(EX_OK);
451c2aa98e2SPeter Wemm 		p = buf + strlen(buf) - 1;
452c2aa98e2SPeter Wemm 		if (p >= buf && *p == '\n')
453c2aa98e2SPeter Wemm 			*p-- = '\0';
454c2aa98e2SPeter Wemm 		if (p >= buf && *p == '\r')
455c2aa98e2SPeter Wemm 			*p-- = '\0';
456c2aa98e2SPeter Wemm 
4573299c2f1SGregory Neil Shapiro 		switch (buf[0])
4583299c2f1SGregory Neil Shapiro 		{
459c2aa98e2SPeter Wemm 		  case 'd':
460c2aa98e2SPeter Wemm 		  case 'D':
46112ed1c7cSGregory Neil Shapiro 			if (sm_strcasecmp(buf, "data") == 0)
4623299c2f1SGregory Neil Shapiro 			{
46312ed1c7cSGregory Neil Shapiro 				bool inbody = false;
464b4662009SGregory Neil Shapiro 
4653299c2f1SGregory Neil Shapiro 				if (rcpt_num == 0)
4663299c2f1SGregory Neil Shapiro 				{
467b4662009SGregory Neil Shapiro 					mailerr("503 5.5.1", "No recipients");
468c2aa98e2SPeter Wemm 					continue;
469c2aa98e2SPeter Wemm 				}
47012ed1c7cSGregory Neil Shapiro 				HoldErrs = true;
47112ed1c7cSGregory Neil Shapiro 				msgfd = store(return_path, &inbody);
47212ed1c7cSGregory Neil Shapiro 				HoldErrs = false;
473b4662009SGregory Neil Shapiro 				if (msgfd < 0 && !inbody)
474b4662009SGregory Neil Shapiro 				{
475b4662009SGregory Neil Shapiro 					flush_error();
476c2aa98e2SPeter Wemm 					continue;
477b4662009SGregory Neil Shapiro 				}
478c2aa98e2SPeter Wemm 
4793299c2f1SGregory Neil Shapiro 				for (i = 0; i < rcpt_num; i++)
4803299c2f1SGregory Neil Shapiro 				{
481b4662009SGregory Neil Shapiro 					if (msgfd < 0)
482b4662009SGregory Neil Shapiro 					{
483b4662009SGregory Neil Shapiro 						/* print error for rcpt */
484b4662009SGregory Neil Shapiro 						flush_error();
485b4662009SGregory Neil Shapiro 						continue;
486b4662009SGregory Neil Shapiro 					}
487c2aa98e2SPeter Wemm 					p = strchr(rcpt_addr[i], '+');
488c2aa98e2SPeter Wemm 					if (p != NULL)
489b4662009SGregory Neil Shapiro 						*p = '\0';
490b4662009SGregory Neil Shapiro 					deliver(msgfd, rcpt_addr[i]);
491c2aa98e2SPeter Wemm 				}
492b4662009SGregory Neil Shapiro 				if (msgfd >= 0)
4933299c2f1SGregory Neil Shapiro 					(void) close(msgfd);
494c2aa98e2SPeter Wemm 				goto rset;
495c2aa98e2SPeter Wemm 			}
496c2aa98e2SPeter Wemm 			goto syntaxerr;
4973299c2f1SGregory Neil Shapiro 			/* NOTREACHED */
4983299c2f1SGregory Neil Shapiro 			break;
499c2aa98e2SPeter Wemm 
500c2aa98e2SPeter Wemm 		  case 'l':
501c2aa98e2SPeter Wemm 		  case 'L':
50212ed1c7cSGregory Neil Shapiro 			if (sm_strncasecmp(buf, "lhlo ", 5) == 0)
5033299c2f1SGregory Neil Shapiro 			{
5043299c2f1SGregory Neil Shapiro 				/* check for duplicate per RFC 1651 4.2 */
5053299c2f1SGregory Neil Shapiro 				if (gotlhlo)
5063299c2f1SGregory Neil Shapiro 				{
507b4662009SGregory Neil Shapiro 					mailerr("503", "%s Duplicate LHLO",
508c2aa98e2SPeter Wemm 					       myhostname);
509c2aa98e2SPeter Wemm 					continue;
510c2aa98e2SPeter Wemm 				}
51112ed1c7cSGregory Neil Shapiro 				gotlhlo = true;
5123299c2f1SGregory Neil Shapiro 				printf("250-%s\r\n", myhostname);
5133299c2f1SGregory Neil Shapiro 				if (EightBitMime)
5143299c2f1SGregory Neil Shapiro 					printf("250-8BITMIME\r\n");
5153299c2f1SGregory Neil Shapiro 				printf("250-ENHANCEDSTATUSCODES\r\n");
5163299c2f1SGregory Neil Shapiro 				printf("250 PIPELINING\r\n");
5173299c2f1SGregory Neil Shapiro 				continue;
5183299c2f1SGregory Neil Shapiro 			}
519c2aa98e2SPeter Wemm 			goto syntaxerr;
5203299c2f1SGregory Neil Shapiro 			/* NOTREACHED */
5213299c2f1SGregory Neil Shapiro 			break;
522c2aa98e2SPeter Wemm 
523c2aa98e2SPeter Wemm 		  case 'm':
524c2aa98e2SPeter Wemm 		  case 'M':
52512ed1c7cSGregory Neil Shapiro 			if (sm_strncasecmp(buf, "mail ", 5) == 0)
5263299c2f1SGregory Neil Shapiro 			{
5273299c2f1SGregory Neil Shapiro 				if (return_path != NULL)
5283299c2f1SGregory Neil Shapiro 				{
529b4662009SGregory Neil Shapiro 					mailerr("503 5.5.1",
530b4662009SGregory Neil Shapiro 						"Nested MAIL command");
531c2aa98e2SPeter Wemm 					continue;
532c2aa98e2SPeter Wemm 				}
53312ed1c7cSGregory Neil Shapiro 				if (sm_strncasecmp(buf + 5, "from:", 5) != 0 ||
5343299c2f1SGregory Neil Shapiro 				    ((return_path = parseaddr(buf + 10,
53512ed1c7cSGregory Neil Shapiro 							      false)) == NULL))
5363299c2f1SGregory Neil Shapiro 				{
537b4662009SGregory Neil Shapiro 					mailerr("501 5.5.4",
538b4662009SGregory Neil Shapiro 						"Syntax error in parameters");
539c2aa98e2SPeter Wemm 					continue;
540c2aa98e2SPeter Wemm 				}
541b4662009SGregory Neil Shapiro 				printf("250 2.5.0 Ok\r\n");
542c2aa98e2SPeter Wemm 				continue;
543c2aa98e2SPeter Wemm 			}
544c2aa98e2SPeter Wemm 			goto syntaxerr;
5453299c2f1SGregory Neil Shapiro 			/* NOTREACHED */
5463299c2f1SGregory Neil Shapiro 			break;
547c2aa98e2SPeter Wemm 
548c2aa98e2SPeter Wemm 		  case 'n':
549c2aa98e2SPeter Wemm 		  case 'N':
55012ed1c7cSGregory Neil Shapiro 			if (sm_strcasecmp(buf, "noop") == 0)
5513299c2f1SGregory Neil Shapiro 			{
552b4662009SGregory Neil Shapiro 				printf("250 2.0.0 Ok\r\n");
553c2aa98e2SPeter Wemm 				continue;
554c2aa98e2SPeter Wemm 			}
555c2aa98e2SPeter Wemm 			goto syntaxerr;
5563299c2f1SGregory Neil Shapiro 			/* NOTREACHED */
5573299c2f1SGregory Neil Shapiro 			break;
558c2aa98e2SPeter Wemm 
559c2aa98e2SPeter Wemm 		  case 'q':
560c2aa98e2SPeter Wemm 		  case 'Q':
56112ed1c7cSGregory Neil Shapiro 			if (sm_strcasecmp(buf, "quit") == 0)
5623299c2f1SGregory Neil Shapiro 			{
563b4662009SGregory Neil Shapiro 				printf("221 2.0.0 Bye\r\n");
564c2aa98e2SPeter Wemm 				exit(EX_OK);
565c2aa98e2SPeter Wemm 			}
566c2aa98e2SPeter Wemm 			goto syntaxerr;
5673299c2f1SGregory Neil Shapiro 			/* NOTREACHED */
5683299c2f1SGregory Neil Shapiro 			break;
569c2aa98e2SPeter Wemm 
570c2aa98e2SPeter Wemm 		  case 'r':
571c2aa98e2SPeter Wemm 		  case 'R':
57212ed1c7cSGregory Neil Shapiro 			if (sm_strncasecmp(buf, "rcpt ", 5) == 0)
5733299c2f1SGregory Neil Shapiro 			{
5743299c2f1SGregory Neil Shapiro 				if (return_path == NULL)
5753299c2f1SGregory Neil Shapiro 				{
576b4662009SGregory Neil Shapiro 					mailerr("503 5.5.1",
577b4662009SGregory Neil Shapiro 						"Need MAIL command");
578c2aa98e2SPeter Wemm 					continue;
579c2aa98e2SPeter Wemm 				}
5803299c2f1SGregory Neil Shapiro 				if (rcpt_num >= rcpt_alloc)
5813299c2f1SGregory Neil Shapiro 				{
582c2aa98e2SPeter Wemm 					rcpt_alloc += RCPT_GROW;
583c2aa98e2SPeter Wemm 					rcpt_addr = (char **)
5843299c2f1SGregory Neil Shapiro 						REALLOC((char *) rcpt_addr,
5853299c2f1SGregory Neil Shapiro 							rcpt_alloc *
5863299c2f1SGregory Neil Shapiro 							sizeof(char **));
5873299c2f1SGregory Neil Shapiro 					if (rcpt_addr == NULL)
5883299c2f1SGregory Neil Shapiro 					{
589b4662009SGregory Neil Shapiro 						mailerr("421 4.3.0",
590b4662009SGregory Neil Shapiro 							"Memory exhausted");
591c2aa98e2SPeter Wemm 						exit(EX_TEMPFAIL);
592c2aa98e2SPeter Wemm 					}
593c2aa98e2SPeter Wemm 				}
59412ed1c7cSGregory Neil Shapiro 				if (sm_strncasecmp(buf + 5, "to:", 3) != 0 ||
5953299c2f1SGregory Neil Shapiro 				    ((rcpt_addr[rcpt_num] = parseaddr(buf + 8,
59612ed1c7cSGregory Neil Shapiro 								      true)) == NULL))
5973299c2f1SGregory Neil Shapiro 				{
598b4662009SGregory Neil Shapiro 					mailerr("501 5.5.4",
599b4662009SGregory Neil Shapiro 						"Syntax error in parameters");
600c2aa98e2SPeter Wemm 					continue;
601c2aa98e2SPeter Wemm 				}
602b4662009SGregory Neil Shapiro 				err = process_recipient(rcpt_addr[rcpt_num]);
603b4662009SGregory Neil Shapiro 				if (err != NULL)
6043299c2f1SGregory Neil Shapiro 				{
605b4662009SGregory Neil Shapiro 					mailerr(NULL, "%s", err);
606c2aa98e2SPeter Wemm 					continue;
607c2aa98e2SPeter Wemm 				}
608c2aa98e2SPeter Wemm 				rcpt_num++;
609b4662009SGregory Neil Shapiro 				printf("250 2.1.5 Ok\r\n");
610c2aa98e2SPeter Wemm 				continue;
611c2aa98e2SPeter Wemm 			}
61212ed1c7cSGregory Neil Shapiro 			else if (sm_strcasecmp(buf, "rset") == 0)
6133299c2f1SGregory Neil Shapiro 			{
614b4662009SGregory Neil Shapiro 				printf("250 2.0.0 Ok\r\n");
615c2aa98e2SPeter Wemm 
616c2aa98e2SPeter Wemm rset:
617c46d91b7SGregory Neil Shapiro 				while (rcpt_num > 0)
618c2aa98e2SPeter Wemm 					free(rcpt_addr[--rcpt_num]);
619c2aa98e2SPeter Wemm 				if (return_path != NULL)
620c2aa98e2SPeter Wemm 					free(return_path);
621c2aa98e2SPeter Wemm 				return_path = NULL;
622c2aa98e2SPeter Wemm 				continue;
623c2aa98e2SPeter Wemm 			}
624c2aa98e2SPeter Wemm 			goto syntaxerr;
6253299c2f1SGregory Neil Shapiro 			/* NOTREACHED */
6263299c2f1SGregory Neil Shapiro 			break;
627c2aa98e2SPeter Wemm 
628c2aa98e2SPeter Wemm 		  case 'v':
629c2aa98e2SPeter Wemm 		  case 'V':
63012ed1c7cSGregory Neil Shapiro 			if (sm_strncasecmp(buf, "vrfy ", 5) == 0)
6313299c2f1SGregory Neil Shapiro 			{
632b4662009SGregory Neil Shapiro 				printf("252 2.3.3 Try RCPT to attempt delivery\r\n");
633c2aa98e2SPeter Wemm 				continue;
634c2aa98e2SPeter Wemm 			}
635c2aa98e2SPeter Wemm 			goto syntaxerr;
6363299c2f1SGregory Neil Shapiro 			/* NOTREACHED */
6373299c2f1SGregory Neil Shapiro 			break;
638c2aa98e2SPeter Wemm 
639c2aa98e2SPeter Wemm 		  default:
640c2aa98e2SPeter Wemm   syntaxerr:
641b4662009SGregory Neil Shapiro 			mailerr("500 5.5.2", "Syntax error");
642c2aa98e2SPeter Wemm 			continue;
6433299c2f1SGregory Neil Shapiro 			/* NOTREACHED */
6443299c2f1SGregory Neil Shapiro 			break;
645c2aa98e2SPeter Wemm 		}
646c2aa98e2SPeter Wemm 	}
647c2aa98e2SPeter Wemm }
648c2aa98e2SPeter Wemm 
649c2aa98e2SPeter Wemm int
65012ed1c7cSGregory Neil Shapiro store(from, inbody)
651c2aa98e2SPeter Wemm 	char *from;
652b4662009SGregory Neil Shapiro 	bool *inbody;
653c2aa98e2SPeter Wemm {
65476b7bf71SPeter Wemm 	FILE *fp = NULL;
655c2aa98e2SPeter Wemm 	time_t tval;
65612ed1c7cSGregory Neil Shapiro 	bool eline;		/* previous line was empty */
65712ed1c7cSGregory Neil Shapiro 	bool fullline = true;	/* current line is terminated */
6583299c2f1SGregory Neil Shapiro 	bool prevfl;		/* previous line was terminated */
659c2aa98e2SPeter Wemm 	char line[2048];
6603299c2f1SGregory Neil Shapiro 	int fd;
661c2aa98e2SPeter Wemm 	char tmpbuf[sizeof _PATH_LOCTMP + 1];
662c2aa98e2SPeter Wemm 
663b4662009SGregory Neil Shapiro 	if (inbody != NULL)
66412ed1c7cSGregory Neil Shapiro 		*inbody = false;
665b4662009SGregory Neil Shapiro 
6663299c2f1SGregory Neil Shapiro 	(void) umask(0077);
66712ed1c7cSGregory Neil Shapiro 	(void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf);
668b4662009SGregory Neil Shapiro 	if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL)
6693299c2f1SGregory Neil Shapiro 	{
6702ef40764SGregory Neil Shapiro 		if (fd >= 0)
6712ef40764SGregory Neil Shapiro 			(void) close(fd);
672b4662009SGregory Neil Shapiro 		mailerr("451 4.3.0", "Unable to open temporary file");
673c2aa98e2SPeter Wemm 		return -1;
6743299c2f1SGregory Neil Shapiro 	}
675c2aa98e2SPeter Wemm 	(void) unlink(tmpbuf);
676c2aa98e2SPeter Wemm 
6773299c2f1SGregory Neil Shapiro 	if (LMTPMode)
6783299c2f1SGregory Neil Shapiro 	{
679b4662009SGregory Neil Shapiro 		printf("354 Go ahead\r\n");
6803299c2f1SGregory Neil Shapiro 		(void) fflush(stdout);
681c2aa98e2SPeter Wemm 	}
682b4662009SGregory Neil Shapiro 	if (inbody != NULL)
68312ed1c7cSGregory Neil Shapiro 		*inbody = true;
684c2aa98e2SPeter Wemm 
685c2aa98e2SPeter Wemm 	(void) time(&tval);
686c2aa98e2SPeter Wemm 	(void) fprintf(fp, "From %s %s", from, ctime(&tval));
687c2aa98e2SPeter Wemm 
6883299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH
6893299c2f1SGregory Neil Shapiro 	HeaderLength = 0;
6903299c2f1SGregory Neil Shapiro 	BodyLength = -1;
6913299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */
6923299c2f1SGregory Neil Shapiro 
693c2aa98e2SPeter Wemm 	line[0] = '\0';
69412ed1c7cSGregory Neil Shapiro 	eline = true;
6953299c2f1SGregory Neil Shapiro 	while (fgets(line, sizeof(line), stdin) != (char *) NULL)
6963299c2f1SGregory Neil Shapiro 	{
6973299c2f1SGregory Neil Shapiro 		size_t line_len = 0;
6983299c2f1SGregory Neil Shapiro 		int peek;
69976b7bf71SPeter Wemm 
7003299c2f1SGregory Neil Shapiro 		prevfl = fullline;	/* preserve state of previous line */
7013299c2f1SGregory Neil Shapiro 		while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
7023299c2f1SGregory Neil Shapiro 			line_len++;
7033299c2f1SGregory Neil Shapiro 		line_len++;
70476b7bf71SPeter Wemm 
7053299c2f1SGregory Neil Shapiro 		/* Check for dot-stuffing */
706b4662009SGregory Neil Shapiro 		if (prevfl && LMTPMode && line[0] == '.')
7073299c2f1SGregory Neil Shapiro 		{
7083299c2f1SGregory Neil Shapiro 			if (line[1] == '\n' ||
7093299c2f1SGregory Neil Shapiro 			    (line[1] == '\r' && line[2] == '\n'))
710c2aa98e2SPeter Wemm 				goto lmtpdot;
7113299c2f1SGregory Neil Shapiro 			memcpy(line, line + 1, line_len);
7123299c2f1SGregory Neil Shapiro 			line_len--;
713c2aa98e2SPeter Wemm 		}
7143299c2f1SGregory Neil Shapiro 
7153299c2f1SGregory Neil Shapiro 		/* Check to see if we have the full line from fgets() */
71612ed1c7cSGregory Neil Shapiro 		fullline = false;
7173299c2f1SGregory Neil Shapiro 		if (line_len > 0)
7183299c2f1SGregory Neil Shapiro 		{
7193299c2f1SGregory Neil Shapiro 			if (line[line_len - 1] == '\n')
7203299c2f1SGregory Neil Shapiro 			{
7213299c2f1SGregory Neil Shapiro 				if (line_len >= 2 &&
7223299c2f1SGregory Neil Shapiro 				    line[line_len - 2] == '\r')
7233299c2f1SGregory Neil Shapiro 				{
7243299c2f1SGregory Neil Shapiro 					line[line_len - 2] = '\n';
7253299c2f1SGregory Neil Shapiro 					line[line_len - 1] = '\0';
7263299c2f1SGregory Neil Shapiro 					line_len--;
7273299c2f1SGregory Neil Shapiro 				}
72812ed1c7cSGregory Neil Shapiro 				fullline = true;
7293299c2f1SGregory Neil Shapiro 			}
7303299c2f1SGregory Neil Shapiro 			else if (line[line_len - 1] == '\r')
7313299c2f1SGregory Neil Shapiro 			{
7323299c2f1SGregory Neil Shapiro 				/* Did we just miss the CRLF? */
7333299c2f1SGregory Neil Shapiro 				peek = fgetc(stdin);
7343299c2f1SGregory Neil Shapiro 				if (peek == '\n')
7353299c2f1SGregory Neil Shapiro 				{
7363299c2f1SGregory Neil Shapiro 					line[line_len - 1] = '\n';
73712ed1c7cSGregory Neil Shapiro 					fullline = true;
7383299c2f1SGregory Neil Shapiro 				}
7393299c2f1SGregory Neil Shapiro 				else
7403299c2f1SGregory Neil Shapiro 					(void) ungetc(peek, stdin);
7413299c2f1SGregory Neil Shapiro 			}
7423299c2f1SGregory Neil Shapiro 		}
7433299c2f1SGregory Neil Shapiro 		else
74412ed1c7cSGregory Neil Shapiro 			fullline = true;
7453299c2f1SGregory Neil Shapiro 
7463299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH
7473299c2f1SGregory Neil Shapiro 		if (prevfl && line[0] == '\n' && HeaderLength == 0)
7483299c2f1SGregory Neil Shapiro 		{
74912ed1c7cSGregory Neil Shapiro 			eline = false;
750b4662009SGregory Neil Shapiro 			if (fp != NULL)
7513299c2f1SGregory Neil Shapiro 				HeaderLength = ftell(fp);
7523299c2f1SGregory Neil Shapiro 			if (HeaderLength <= 0)
7533299c2f1SGregory Neil Shapiro 			{
7543299c2f1SGregory Neil Shapiro 				/*
7553299c2f1SGregory Neil Shapiro 				**  shouldn't happen, unless ftell() is
7563299c2f1SGregory Neil Shapiro 				**  badly broken
7573299c2f1SGregory Neil Shapiro 				*/
7583299c2f1SGregory Neil Shapiro 
7593299c2f1SGregory Neil Shapiro 				HeaderLength = -1;
7603299c2f1SGregory Neil Shapiro 			}
7613299c2f1SGregory Neil Shapiro 		}
7623299c2f1SGregory Neil Shapiro #else /* CONTENTLENGTH */
7633299c2f1SGregory Neil Shapiro 		if (prevfl && line[0] == '\n')
76412ed1c7cSGregory Neil Shapiro 			eline = true;
7653299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */
7663299c2f1SGregory Neil Shapiro 		else
7673299c2f1SGregory Neil Shapiro 		{
768c2aa98e2SPeter Wemm 			if (eline && line[0] == 'F' &&
769b4662009SGregory Neil Shapiro 			    fp != NULL &&
770c2aa98e2SPeter Wemm 			    !memcmp(line, "From ", 5))
771c2aa98e2SPeter Wemm 				(void) putc('>', fp);
77212ed1c7cSGregory Neil Shapiro 			eline = false;
7733299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH
7743299c2f1SGregory Neil Shapiro 			/* discard existing "Content-Length:" headers */
7753299c2f1SGregory Neil Shapiro 			if (prevfl && HeaderLength == 0 &&
7763299c2f1SGregory Neil Shapiro 			    (line[0] == 'C' || line[0] == 'c') &&
77712ed1c7cSGregory Neil Shapiro 			    sm_strncasecmp(line, ContentHdr, 15) == 0)
7783299c2f1SGregory Neil Shapiro 			{
7793299c2f1SGregory Neil Shapiro 				/*
7803299c2f1SGregory Neil Shapiro 				**  be paranoid: clear the line
7813299c2f1SGregory Neil Shapiro 				**  so no "wrong matches" may occur later
7823299c2f1SGregory Neil Shapiro 				*/
7833299c2f1SGregory Neil Shapiro 				line[0] = '\0';
7843299c2f1SGregory Neil Shapiro 				continue;
785c2aa98e2SPeter Wemm 			}
7863299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */
7873299c2f1SGregory Neil Shapiro 
7883299c2f1SGregory Neil Shapiro 		}
789b4662009SGregory Neil Shapiro 		if (fp != NULL)
790b4662009SGregory Neil Shapiro 		{
7913299c2f1SGregory Neil Shapiro 			(void) fwrite(line, sizeof(char), line_len, fp);
7923299c2f1SGregory Neil Shapiro 			if (ferror(fp))
7933299c2f1SGregory Neil Shapiro 			{
79476b7bf71SPeter Wemm 				mailerr("451 4.3.0",
795b4662009SGregory Neil Shapiro 					"Temporary file write error");
7963299c2f1SGregory Neil Shapiro 				(void) fclose(fp);
797b4662009SGregory Neil Shapiro 				fp = NULL;
798b4662009SGregory Neil Shapiro 				continue;
799c2aa98e2SPeter Wemm 			}
800c2aa98e2SPeter Wemm 		}
801c2aa98e2SPeter Wemm 	}
802c2aa98e2SPeter Wemm 
803b4662009SGregory Neil Shapiro 	/* check if an error occurred */
804b4662009SGregory Neil Shapiro 	if (fp == NULL)
805b4662009SGregory Neil Shapiro 		return -1;
806b4662009SGregory Neil Shapiro 
807b4662009SGregory Neil Shapiro 	if (LMTPMode)
8083299c2f1SGregory Neil Shapiro 	{
809c2aa98e2SPeter Wemm 		/* Got a premature EOF -- toss message and exit */
810c2aa98e2SPeter Wemm 		exit(EX_OK);
811c2aa98e2SPeter Wemm 	}
812c2aa98e2SPeter Wemm 
813c2aa98e2SPeter Wemm 	/* If message not newline terminated, need an extra. */
814b4662009SGregory Neil Shapiro 	if (fp != NULL && strchr(line, '\n') == NULL)
815c2aa98e2SPeter Wemm 		(void) putc('\n', fp);
816c2aa98e2SPeter Wemm 
817c2aa98e2SPeter Wemm   lmtpdot:
818c2aa98e2SPeter Wemm 
8193299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH
820b4662009SGregory Neil Shapiro 	if (fp != NULL)
8213299c2f1SGregory Neil Shapiro 		BodyLength = ftell(fp);
8223299c2f1SGregory Neil Shapiro 	if (HeaderLength == 0 && BodyLength > 0)	/* empty body */
8233299c2f1SGregory Neil Shapiro 	{
8243299c2f1SGregory Neil Shapiro 		HeaderLength = BodyLength;
8253299c2f1SGregory Neil Shapiro 		BodyLength = 0;
8263299c2f1SGregory Neil Shapiro 	}
8273299c2f1SGregory Neil Shapiro 	else
8283299c2f1SGregory Neil Shapiro 		BodyLength = BodyLength - HeaderLength - 1 ;
8293299c2f1SGregory Neil Shapiro 
8303299c2f1SGregory Neil Shapiro 	if (HeaderLength > 0 && BodyLength >= 0)
8313299c2f1SGregory Neil Shapiro 	{
83212ed1c7cSGregory Neil Shapiro 		(void) sm_snprintf(line, sizeof line, "%lld\n",
83312ed1c7cSGregory Neil Shapiro 				   (LONGLONG_T) BodyLength);
83412ed1c7cSGregory Neil Shapiro 		(void) sm_strlcpy(&ContentHdr[16], line,
83512ed1c7cSGregory Neil Shapiro 				  sizeof(ContentHdr) - 16);
8363299c2f1SGregory Neil Shapiro 	}
8373299c2f1SGregory Neil Shapiro 	else
8383299c2f1SGregory Neil Shapiro 		BodyLength = -1;	/* Something is wrong here */
8393299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */
8403299c2f1SGregory Neil Shapiro 
841c2aa98e2SPeter Wemm 	/* Output a newline; note, empty messages are allowed. */
842b4662009SGregory Neil Shapiro 	if (fp != NULL)
843c2aa98e2SPeter Wemm 		(void) putc('\n', fp);
844c2aa98e2SPeter Wemm 
845b4662009SGregory Neil Shapiro 	if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0)
8463299c2f1SGregory Neil Shapiro 	{
847b4662009SGregory Neil Shapiro 		mailerr("451 4.3.0", "Temporary file write error");
848b4662009SGregory Neil Shapiro 		if (fp != NULL)
8493299c2f1SGregory Neil Shapiro 			(void) fclose(fp);
850c2aa98e2SPeter Wemm 		return -1;
8513299c2f1SGregory Neil Shapiro 	}
8523299c2f1SGregory Neil Shapiro 	return fd;
853c2aa98e2SPeter Wemm }
854c2aa98e2SPeter Wemm 
855c2aa98e2SPeter Wemm void
856b4662009SGregory Neil Shapiro deliver(fd, name)
857c2aa98e2SPeter Wemm 	int fd;
858c2aa98e2SPeter Wemm 	char *name;
859c2aa98e2SPeter Wemm {
8603299c2f1SGregory Neil Shapiro 	struct stat fsb;
8613299c2f1SGregory Neil Shapiro 	struct stat sb;
8623299c2f1SGregory Neil Shapiro 	char path[MAXPATHLEN];
863c46d91b7SGregory Neil Shapiro 	int mbfd = -1, nr = 0, nw, off;
86412ed1c7cSGregory Neil Shapiro 	int exitval;
865c2aa98e2SPeter Wemm 	char *p;
866b4662009SGregory Neil Shapiro 	char *errcode;
8677660b554SGregory Neil Shapiro 	off_t curoff, cursize;
8683299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH
8693299c2f1SGregory Neil Shapiro 	off_t headerbytes;
8703299c2f1SGregory Neil Shapiro 	int readamount;
8713299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */
8723299c2f1SGregory Neil Shapiro 	char biffmsg[100], buf[8 * 1024];
87312ed1c7cSGregory Neil Shapiro 	SM_MBDB_T user;
8743299c2f1SGregory Neil Shapiro 
875c2aa98e2SPeter Wemm 	/*
8763299c2f1SGregory Neil Shapiro 	**  Disallow delivery to unknown names -- special mailboxes can be
8773299c2f1SGregory Neil Shapiro 	**  handled in the sendmail aliases file.
878c2aa98e2SPeter Wemm 	*/
879b4662009SGregory Neil Shapiro 
88012ed1c7cSGregory Neil Shapiro 	exitval = sm_mbdb_lookup(name, &user);
88112ed1c7cSGregory Neil Shapiro 	switch (exitval)
8823299c2f1SGregory Neil Shapiro 	{
88312ed1c7cSGregory Neil Shapiro 	  case EX_OK:
88412ed1c7cSGregory Neil Shapiro 		break;
88512ed1c7cSGregory Neil Shapiro 
88612ed1c7cSGregory Neil Shapiro 	  case EX_NOUSER:
88712ed1c7cSGregory Neil Shapiro 		exitval = EX_UNAVAILABLE;
88812ed1c7cSGregory Neil Shapiro 		mailerr("550 5.1.1", "%s: User unknown", name);
88912ed1c7cSGregory Neil Shapiro 		break;
89012ed1c7cSGregory Neil Shapiro 
89112ed1c7cSGregory Neil Shapiro 	  case EX_TEMPFAIL:
89212ed1c7cSGregory Neil Shapiro 		mailerr("451 4.3.0", "%s: User database failure; retry later",
89312ed1c7cSGregory Neil Shapiro 			name);
89412ed1c7cSGregory Neil Shapiro 		break;
89512ed1c7cSGregory Neil Shapiro 
89612ed1c7cSGregory Neil Shapiro 	  default:
89712ed1c7cSGregory Neil Shapiro 		exitval = EX_UNAVAILABLE;
89812ed1c7cSGregory Neil Shapiro 		mailerr("550 5.3.0", "%s: User database failure", name);
89912ed1c7cSGregory Neil Shapiro 		break;
900c2aa98e2SPeter Wemm 	}
90112ed1c7cSGregory Neil Shapiro 
90212ed1c7cSGregory Neil Shapiro 	if (exitval != EX_OK)
90312ed1c7cSGregory Neil Shapiro 	{
90412ed1c7cSGregory Neil Shapiro 		if (ExitVal != EX_TEMPFAIL)
90512ed1c7cSGregory Neil Shapiro 			ExitVal = exitval;
906c2aa98e2SPeter Wemm 		return;
907c2aa98e2SPeter Wemm 	}
90812ed1c7cSGregory Neil Shapiro 
909c2aa98e2SPeter Wemm 	endpwent();
910c2aa98e2SPeter Wemm 
911c2aa98e2SPeter Wemm 	/*
9123299c2f1SGregory Neil Shapiro 	**  Keep name reasonably short to avoid buffer overruns.
9133299c2f1SGregory Neil Shapiro 	**	This isn't necessary on BSD because of the proper
9143299c2f1SGregory Neil Shapiro 	**	definition of snprintf(), but it can cause problems
9153299c2f1SGregory Neil Shapiro 	**	on other systems.
9163299c2f1SGregory Neil Shapiro 	**  Also, clear out any bogus characters.
917c2aa98e2SPeter Wemm 	*/
918c2aa98e2SPeter Wemm 
919c2aa98e2SPeter Wemm 	if (strlen(name) > 40)
920c2aa98e2SPeter Wemm 		name[40] = '\0';
921c2aa98e2SPeter Wemm 	for (p = name; *p != '\0'; p++)
922c2aa98e2SPeter Wemm 	{
923c2aa98e2SPeter Wemm 		if (!isascii(*p))
924c2aa98e2SPeter Wemm 			*p &= 0x7f;
925c2aa98e2SPeter Wemm 		else if (!isprint(*p))
926c2aa98e2SPeter Wemm 			*p = '.';
927c2aa98e2SPeter Wemm 	}
928c2aa98e2SPeter Wemm 
9293299c2f1SGregory Neil Shapiro 
93012ed1c7cSGregory Neil Shapiro 	if (HomeMailFile == NULL)
93112ed1c7cSGregory Neil Shapiro 	{
93212ed1c7cSGregory Neil Shapiro 		if (sm_snprintf(path, sizeof(path), "%s/%s",
93312ed1c7cSGregory Neil Shapiro 				_PATH_MAILDIR, name) >= sizeof(path))
93412ed1c7cSGregory Neil Shapiro 		{
93512ed1c7cSGregory Neil Shapiro 			exitval = EX_UNAVAILABLE;
93612ed1c7cSGregory Neil Shapiro 			mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
93712ed1c7cSGregory Neil Shapiro 			return;
93812ed1c7cSGregory Neil Shapiro 		}
93912ed1c7cSGregory Neil Shapiro 	}
94012ed1c7cSGregory Neil Shapiro 	else if (*user.mbdb_homedir == '\0')
94112ed1c7cSGregory Neil Shapiro 	{
94212ed1c7cSGregory Neil Shapiro 		exitval = EX_UNAVAILABLE;
94312ed1c7cSGregory Neil Shapiro 		mailerr("550 5.1.1", "%s: User missing home directory", name);
94412ed1c7cSGregory Neil Shapiro 		return;
94512ed1c7cSGregory Neil Shapiro 	}
94612ed1c7cSGregory Neil Shapiro 	else if (sm_snprintf(path, sizeof(path), "%s/%s",
94712ed1c7cSGregory Neil Shapiro 			     user.mbdb_homedir, HomeMailFile) >= sizeof(path))
94812ed1c7cSGregory Neil Shapiro 	{
94912ed1c7cSGregory Neil Shapiro 		exitval = EX_UNAVAILABLE;
95012ed1c7cSGregory Neil Shapiro 		mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
95112ed1c7cSGregory Neil Shapiro 		return;
95212ed1c7cSGregory Neil Shapiro 	}
953c2aa98e2SPeter Wemm 
9543299c2f1SGregory Neil Shapiro 
955c2aa98e2SPeter Wemm 	/*
9563299c2f1SGregory Neil Shapiro 	**  If the mailbox is linked or a symlink, fail.  There's an obvious
9573299c2f1SGregory Neil Shapiro 	**  race here, that the file was replaced with a symbolic link after
9583299c2f1SGregory Neil Shapiro 	**  the lstat returned, but before the open.  We attempt to detect
9593299c2f1SGregory Neil Shapiro 	**  this by comparing the original stat information and information
9603299c2f1SGregory Neil Shapiro 	**  returned by an fstat of the file descriptor returned by the open.
9613299c2f1SGregory Neil Shapiro 	**
9623299c2f1SGregory Neil Shapiro 	**  NB: this is a symptom of a larger problem, that the mail spooling
9633299c2f1SGregory Neil Shapiro 	**  directory is writeable by the wrong users.  If that directory is
9643299c2f1SGregory Neil Shapiro 	**  writeable, system security is compromised for other reasons, and
9653299c2f1SGregory Neil Shapiro 	**  it cannot be fixed here.
9663299c2f1SGregory Neil Shapiro 	**
9673299c2f1SGregory Neil Shapiro 	**  If we created the mailbox, set the owner/group.  If that fails,
9683299c2f1SGregory Neil Shapiro 	**  just return.  Another process may have already opened it, so we
9693299c2f1SGregory Neil Shapiro 	**  can't unlink it.  Historically, binmail set the owner/group at
9703299c2f1SGregory Neil Shapiro 	**  each mail delivery.  We no longer do this, assuming that if the
9713299c2f1SGregory Neil Shapiro 	**  ownership or permissions were changed there was a reason.
9723299c2f1SGregory Neil Shapiro 	**
9733299c2f1SGregory Neil Shapiro 	**  XXX
9743299c2f1SGregory Neil Shapiro 	**  open(2) should support flock'ing the file.
975c2aa98e2SPeter Wemm 	*/
9763299c2f1SGregory Neil Shapiro 
977c2aa98e2SPeter Wemm tryagain:
9783299c2f1SGregory Neil Shapiro #ifdef MAILLOCK
9793299c2f1SGregory Neil Shapiro 	p = name;
9803299c2f1SGregory Neil Shapiro #else /* MAILLOCK */
9813299c2f1SGregory Neil Shapiro 	p = path;
9823299c2f1SGregory Neil Shapiro #endif /* MAILLOCK */
9833299c2f1SGregory Neil Shapiro 	if ((off = lockmbox(p)) != 0)
9843299c2f1SGregory Neil Shapiro 	{
9853299c2f1SGregory Neil Shapiro 		if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL)
9863299c2f1SGregory Neil Shapiro 		{
9873299c2f1SGregory Neil Shapiro 			ExitVal = EX_TEMPFAIL;
988b4662009SGregory Neil Shapiro 			errcode = "451 4.3.0";
9893299c2f1SGregory Neil Shapiro 		}
9903299c2f1SGregory Neil Shapiro 		else
991b4662009SGregory Neil Shapiro 			errcode = "551 5.3.0";
992b4662009SGregory Neil Shapiro 
993b4662009SGregory Neil Shapiro 		mailerr(errcode, "lockmailbox %s failed; error code %d %s",
99412ed1c7cSGregory Neil Shapiro 			p, off, errno > 0 ? sm_errstring(errno) : "");
9953299c2f1SGregory Neil Shapiro 		return;
9963299c2f1SGregory Neil Shapiro 	}
9973299c2f1SGregory Neil Shapiro 
998c2aa98e2SPeter Wemm 	if (lstat(path, &sb) < 0)
999c2aa98e2SPeter Wemm 	{
10003299c2f1SGregory Neil Shapiro 		int save_errno;
10013299c2f1SGregory Neil Shapiro 		int mode = S_IRUSR|S_IWUSR;
100212ed1c7cSGregory Neil Shapiro 		gid_t gid = user.mbdb_gid;
10033299c2f1SGregory Neil Shapiro 
10043299c2f1SGregory Neil Shapiro #ifdef MAILGID
10053299c2f1SGregory Neil Shapiro 		(void) umask(0007);
10063299c2f1SGregory Neil Shapiro 		gid = MAILGID;
10073299c2f1SGregory Neil Shapiro 		mode |= S_IRGRP|S_IWGRP;
10083299c2f1SGregory Neil Shapiro #endif /* MAILGID */
10093299c2f1SGregory Neil Shapiro 
10107660b554SGregory Neil Shapiro 		mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY,
1011d995d2baSGregory Neil Shapiro 			    mode);
10123299c2f1SGregory Neil Shapiro 		save_errno = errno;
10133299c2f1SGregory Neil Shapiro 
10143299c2f1SGregory Neil Shapiro 		if (lstat(path, &sb) < 0)
10153299c2f1SGregory Neil Shapiro 		{
10163299c2f1SGregory Neil Shapiro 			ExitVal = EX_CANTCREAT;
101776b7bf71SPeter Wemm 			mailerr("550 5.2.0",
101876b7bf71SPeter Wemm 				"%s: lstat: file changed after open", path);
1019c2aa98e2SPeter Wemm 			goto err1;
1020c2aa98e2SPeter Wemm 		}
1021b4662009SGregory Neil Shapiro 		if (mbfd < 0)
10223299c2f1SGregory Neil Shapiro 		{
10233299c2f1SGregory Neil Shapiro 			if (save_errno == EEXIST)
1024c2aa98e2SPeter Wemm 				goto tryagain;
1025d995d2baSGregory Neil Shapiro 
1026d995d2baSGregory Neil Shapiro 			/* open failed, don't try again */
1027d995d2baSGregory Neil Shapiro 			mailerr("450 4.2.0", "%s: %s", path,
102812ed1c7cSGregory Neil Shapiro 				sm_errstring(save_errno));
1029d995d2baSGregory Neil Shapiro 			goto err0;
10303299c2f1SGregory Neil Shapiro 		}
103112ed1c7cSGregory Neil Shapiro 		else if (fchown(mbfd, user.mbdb_uid, gid) < 0)
10323299c2f1SGregory Neil Shapiro 		{
1033c2aa98e2SPeter Wemm 			mailerr("451 4.3.0", "chown %u.%u: %s",
103412ed1c7cSGregory Neil Shapiro 				user.mbdb_uid, gid, name);
1035c2aa98e2SPeter Wemm 			goto err1;
1036c2aa98e2SPeter Wemm 		}
1037d995d2baSGregory Neil Shapiro 		else
1038d995d2baSGregory Neil Shapiro 		{
1039d995d2baSGregory Neil Shapiro 			/*
1040d995d2baSGregory Neil Shapiro 			**  open() was successful, now close it so can
1041d995d2baSGregory Neil Shapiro 			**  be opened as the right owner again.
1042d995d2baSGregory Neil Shapiro 			**  Paranoia: reset mbdf since the file descriptor
1043d995d2baSGregory Neil Shapiro 			**  is no longer valid; better safe than sorry.
1044d995d2baSGregory Neil Shapiro 			*/
1045d995d2baSGregory Neil Shapiro 
104612ed1c7cSGregory Neil Shapiro 			sb.st_uid = user.mbdb_uid;
1047d995d2baSGregory Neil Shapiro 			(void) close(mbfd);
1048d995d2baSGregory Neil Shapiro 			mbfd = -1;
1049d995d2baSGregory Neil Shapiro 		}
10503299c2f1SGregory Neil Shapiro 	}
1051f9218d3dSGregory Neil Shapiro 	else if (sb.st_nlink != 1)
1052f9218d3dSGregory Neil Shapiro 	{
1053f9218d3dSGregory Neil Shapiro 		mailerr("550 5.2.0", "%s: too many links", path);
1054f9218d3dSGregory Neil Shapiro 		goto err0;
1055f9218d3dSGregory Neil Shapiro 	}
1056f9218d3dSGregory Neil Shapiro 	else if (!S_ISREG(sb.st_mode))
10573299c2f1SGregory Neil Shapiro 	{
1058c2aa98e2SPeter Wemm 		mailerr("550 5.2.0", "%s: irregular file", path);
1059c2aa98e2SPeter Wemm 		goto err0;
10603299c2f1SGregory Neil Shapiro 	}
106112ed1c7cSGregory Neil Shapiro 	else if (sb.st_uid != user.mbdb_uid)
10623299c2f1SGregory Neil Shapiro 	{
10633299c2f1SGregory Neil Shapiro 		ExitVal = EX_CANTCREAT;
1064c2aa98e2SPeter Wemm 		mailerr("550 5.2.0", "%s: wrong ownership (%d)",
106512ed1c7cSGregory Neil Shapiro 			path, (int) sb.st_uid);
1066c2aa98e2SPeter Wemm 		goto err0;
1067c2aa98e2SPeter Wemm 	}
1068c2aa98e2SPeter Wemm 
1069d995d2baSGregory Neil Shapiro 	/* change UID for quota checks */
107012ed1c7cSGregory Neil Shapiro 	if (setreuid(0, user.mbdb_uid) < 0)
1071d995d2baSGregory Neil Shapiro 	{
1072d995d2baSGregory Neil Shapiro 		mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
107312ed1c7cSGregory Neil Shapiro 			(int) user.mbdb_uid, sm_errstring(errno),
107412ed1c7cSGregory Neil Shapiro 			(int) getuid(), (int) geteuid());
1075d995d2baSGregory Neil Shapiro 		goto err1;
1076d995d2baSGregory Neil Shapiro 	}
1077d995d2baSGregory Neil Shapiro #ifdef DEBUG
107812ed1c7cSGregory Neil Shapiro 	fprintf(stderr, "new euid = %d\n", (int) geteuid());
1079d995d2baSGregory Neil Shapiro #endif /* DEBUG */
10807660b554SGregory Neil Shapiro 	mbfd = open(path, O_APPEND|O_WRONLY, 0);
1081d995d2baSGregory Neil Shapiro 	if (mbfd < 0)
10823299c2f1SGregory Neil Shapiro 	{
108312ed1c7cSGregory Neil Shapiro 		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
1084c2aa98e2SPeter Wemm 		goto err0;
10853299c2f1SGregory Neil Shapiro 	}
10863299c2f1SGregory Neil Shapiro 	else if (fstat(mbfd, &fsb) < 0 ||
1087c2aa98e2SPeter Wemm 		 fsb.st_nlink != 1 ||
1088c2aa98e2SPeter Wemm 		 sb.st_nlink != 1 ||
1089c2aa98e2SPeter Wemm 		 !S_ISREG(fsb.st_mode) ||
1090c2aa98e2SPeter Wemm 		 sb.st_dev != fsb.st_dev ||
1091c2aa98e2SPeter Wemm 		 sb.st_ino != fsb.st_ino ||
1092c2aa98e2SPeter Wemm # if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
1093c2aa98e2SPeter Wemm 		 sb.st_gen != fsb.st_gen ||
10943299c2f1SGregory Neil Shapiro # endif /* HAS_ST_GEN && 0 */
10953299c2f1SGregory Neil Shapiro 		 sb.st_uid != fsb.st_uid)
10963299c2f1SGregory Neil Shapiro 	{
10973299c2f1SGregory Neil Shapiro 		ExitVal = EX_TEMPFAIL;
109876b7bf71SPeter Wemm 		mailerr("550 5.2.0", "%s: fstat: file changed after open",
109976b7bf71SPeter Wemm 			path);
1100c2aa98e2SPeter Wemm 		goto err1;
1101c2aa98e2SPeter Wemm 	}
1102c2aa98e2SPeter Wemm 
110312ed1c7cSGregory Neil Shapiro #if 0
110412ed1c7cSGregory Neil Shapiro 	/*
110512ed1c7cSGregory Neil Shapiro 	**  This code could be reused if we decide to add a
110612ed1c7cSGregory Neil Shapiro 	**  per-user quota field to the sm_mbdb interface.
110712ed1c7cSGregory Neil Shapiro 	*/
110812ed1c7cSGregory Neil Shapiro 
110912ed1c7cSGregory Neil Shapiro 	/*
111012ed1c7cSGregory Neil Shapiro 	**  Fail if the user has a quota specified, and delivery of this
111112ed1c7cSGregory Neil Shapiro 	**  message would exceed that quota.  We bounce such failures using
111212ed1c7cSGregory Neil Shapiro 	**  EX_UNAVAILABLE, unless there were internal problems, since
111312ed1c7cSGregory Neil Shapiro 	**  storing immense messages for later retries can cause queueing
111412ed1c7cSGregory Neil Shapiro 	**  issues.
111512ed1c7cSGregory Neil Shapiro 	*/
111612ed1c7cSGregory Neil Shapiro 
111712ed1c7cSGregory Neil Shapiro 	if (ui.quota > 0)
111812ed1c7cSGregory Neil Shapiro 	{
111912ed1c7cSGregory Neil Shapiro 		struct stat dsb;
112012ed1c7cSGregory Neil Shapiro 
112112ed1c7cSGregory Neil Shapiro 		if (fstat(fd, &dsb) < 0)
112212ed1c7cSGregory Neil Shapiro 		{
112312ed1c7cSGregory Neil Shapiro 			ExitVal = EX_TEMPFAIL;
112412ed1c7cSGregory Neil Shapiro 			mailerr("451 4.3.0",
112512ed1c7cSGregory Neil Shapiro 				"%s: fstat: can't stat temporary storage: %s",
112612ed1c7cSGregory Neil Shapiro 				ui.mailspool, sm_errstring(errno));
112712ed1c7cSGregory Neil Shapiro 			goto err1;
112812ed1c7cSGregory Neil Shapiro 		}
112912ed1c7cSGregory Neil Shapiro 
113012ed1c7cSGregory Neil Shapiro 		if (dsb.st_size + sb.st_size + 1 > ui.quota)
113112ed1c7cSGregory Neil Shapiro 		{
113212ed1c7cSGregory Neil Shapiro 			ExitVal = EX_UNAVAILABLE;
113312ed1c7cSGregory Neil Shapiro 			mailerr("551 5.2.2",
113412ed1c7cSGregory Neil Shapiro 				"%s: Mailbox full or quota exceeded",
113512ed1c7cSGregory Neil Shapiro 				ui.mailspool);
113612ed1c7cSGregory Neil Shapiro 			goto err1;
113712ed1c7cSGregory Neil Shapiro 		}
113812ed1c7cSGregory Neil Shapiro 	}
113912ed1c7cSGregory Neil Shapiro #endif /* 0 */
11403299c2f1SGregory Neil Shapiro 
1141c2aa98e2SPeter Wemm 	/* Wait until we can get a lock on the file. */
11423299c2f1SGregory Neil Shapiro 	if (flock(mbfd, LOCK_EX) < 0)
11433299c2f1SGregory Neil Shapiro 	{
114412ed1c7cSGregory Neil Shapiro 		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
1145c2aa98e2SPeter Wemm 		goto err1;
1146c2aa98e2SPeter Wemm 	}
1147c2aa98e2SPeter Wemm 
114872936242SGregory Neil Shapiro 	/* Get the starting offset of the new message */
1149c2aa98e2SPeter Wemm 	curoff = lseek(mbfd, (off_t) 0, SEEK_END);
1150518536daSGregory Neil Shapiro 
1151518536daSGregory Neil Shapiro 	if (!nobiff)
1152518536daSGregory Neil Shapiro 	{
115312ed1c7cSGregory Neil Shapiro 		(void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n",
115412ed1c7cSGregory Neil Shapiro 				   name, (LONGLONG_T) curoff);
1155d615a192SPeter Wemm 	}
1156c2aa98e2SPeter Wemm 
1157c2aa98e2SPeter Wemm 	/* Copy the message into the file. */
11583299c2f1SGregory Neil Shapiro 	if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1)
11593299c2f1SGregory Neil Shapiro 	{
1160b4662009SGregory Neil Shapiro 		mailerr("450 4.2.0", "Temporary file: %s",
116112ed1c7cSGregory Neil Shapiro 			sm_errstring(errno));
1162c2aa98e2SPeter Wemm 		goto err1;
1163c2aa98e2SPeter Wemm 	}
1164c2aa98e2SPeter Wemm #ifdef DEBUG
116512ed1c7cSGregory Neil Shapiro 	fprintf(stderr, "before writing: euid = %d\n", (int) geteuid());
11663299c2f1SGregory Neil Shapiro #endif /* DEBUG */
11673299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH
11683299c2f1SGregory Neil Shapiro 	headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ;
11693299c2f1SGregory Neil Shapiro 	for (;;)
11703299c2f1SGregory Neil Shapiro 	{
11713299c2f1SGregory Neil Shapiro 		if (headerbytes == 0)
11723299c2f1SGregory Neil Shapiro 		{
117312ed1c7cSGregory Neil Shapiro 			(void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr);
11743299c2f1SGregory Neil Shapiro 			nr = strlen(buf);
11753299c2f1SGregory Neil Shapiro 			headerbytes = -1;
11763299c2f1SGregory Neil Shapiro 			readamount = 0;
11773299c2f1SGregory Neil Shapiro 		}
11783299c2f1SGregory Neil Shapiro 		else if (headerbytes > sizeof(buf) || headerbytes < 0)
11793299c2f1SGregory Neil Shapiro 			readamount = sizeof(buf);
11803299c2f1SGregory Neil Shapiro 		else
11813299c2f1SGregory Neil Shapiro 			readamount = headerbytes;
11823299c2f1SGregory Neil Shapiro 		if (readamount != 0)
11833299c2f1SGregory Neil Shapiro 			nr = read(fd, buf, readamount);
11843299c2f1SGregory Neil Shapiro 		if (nr <= 0)
11853299c2f1SGregory Neil Shapiro 			break;
11863299c2f1SGregory Neil Shapiro 		if (headerbytes > 0)
11873299c2f1SGregory Neil Shapiro 			headerbytes -= nr ;
11883299c2f1SGregory Neil Shapiro 
11893299c2f1SGregory Neil Shapiro #else /* CONTENTLENGTH */
1190c2aa98e2SPeter Wemm 	while ((nr = read(fd, buf, sizeof(buf))) > 0)
11913299c2f1SGregory Neil Shapiro 	{
11923299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */
1193c2aa98e2SPeter Wemm 		for (off = 0; off < nr; off += nw)
11943299c2f1SGregory Neil Shapiro 		{
11953299c2f1SGregory Neil Shapiro 			if ((nw = write(mbfd, buf + off, nr - off)) < 0)
11963299c2f1SGregory Neil Shapiro 			{
1197b4662009SGregory Neil Shapiro 				errcode = "450 4.2.0";
11983299c2f1SGregory Neil Shapiro #ifdef EDQUOT
1199b4662009SGregory Neil Shapiro 				if (errno == EDQUOT && BounceQuota)
1200b4662009SGregory Neil Shapiro 					errcode = "552 5.2.2";
12013299c2f1SGregory Neil Shapiro #endif /* EDQUOT */
1202b4662009SGregory Neil Shapiro 				mailerr(errcode, "%s: %s",
120312ed1c7cSGregory Neil Shapiro 					path, sm_errstring(errno));
1204c2aa98e2SPeter Wemm 				goto err3;
1205c2aa98e2SPeter Wemm 			}
12063299c2f1SGregory Neil Shapiro 		}
12073299c2f1SGregory Neil Shapiro 	}
12083299c2f1SGregory Neil Shapiro 	if (nr < 0)
12093299c2f1SGregory Neil Shapiro 	{
1210b4662009SGregory Neil Shapiro 		mailerr("450 4.2.0", "Temporary file: %s",
121112ed1c7cSGregory Neil Shapiro 			sm_errstring(errno));
1212c2aa98e2SPeter Wemm 		goto err3;
1213c2aa98e2SPeter Wemm 	}
1214c2aa98e2SPeter Wemm 
1215c2aa98e2SPeter Wemm 	/* Flush to disk, don't wait for update. */
12163299c2f1SGregory Neil Shapiro 	if (!nofsync && fsync(mbfd) < 0)
12173299c2f1SGregory Neil Shapiro 	{
121812ed1c7cSGregory Neil Shapiro 		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
1219c2aa98e2SPeter Wemm err3:
1220c2aa98e2SPeter Wemm #ifdef DEBUG
122112ed1c7cSGregory Neil Shapiro 		fprintf(stderr, "reset euid = %d\n", (int) geteuid());
12223299c2f1SGregory Neil Shapiro #endif /* DEBUG */
12232ef40764SGregory Neil Shapiro 		if (mbfd >= 0)
1224c2aa98e2SPeter Wemm 			(void) ftruncate(mbfd, curoff);
1225d995d2baSGregory Neil Shapiro err1:		if (mbfd >= 0)
1226d995d2baSGregory Neil Shapiro 			(void) close(mbfd);
12277660b554SGregory Neil Shapiro err0:		(void) setreuid(0, 0);
12287660b554SGregory Neil Shapiro 		unlockmbox();
1229c2aa98e2SPeter Wemm 		return;
1230c2aa98e2SPeter Wemm 	}
1231c2aa98e2SPeter Wemm 
12327660b554SGregory Neil Shapiro 	/*
12337660b554SGregory Neil Shapiro 	**  Save the current size so if the close() fails below
12347660b554SGregory Neil Shapiro 	**  we can make sure no other process has changed the mailbox
12357660b554SGregory Neil Shapiro 	**  between the failed close and the re-open()/re-lock().
12367660b554SGregory Neil Shapiro 	**  If something else has changed the size, we shouldn't
12377660b554SGregory Neil Shapiro 	**  try to truncate it as we may do more harm then good
12387660b554SGregory Neil Shapiro 	**  (e.g., truncate a later message delivery).
12397660b554SGregory Neil Shapiro 	*/
12407660b554SGregory Neil Shapiro 
12417660b554SGregory Neil Shapiro 	if (fstat(mbfd, &sb) < 0)
12427660b554SGregory Neil Shapiro 		cursize = 0;
12437660b554SGregory Neil Shapiro 	else
12447660b554SGregory Neil Shapiro 		cursize = sb.st_size;
12457660b554SGregory Neil Shapiro 
12467660b554SGregory Neil Shapiro 
1247c2aa98e2SPeter Wemm 	/* Close and check -- NFS doesn't write until the close. */
12483299c2f1SGregory Neil Shapiro 	if (close(mbfd))
12493299c2f1SGregory Neil Shapiro 	{
1250b4662009SGregory Neil Shapiro 		errcode = "450 4.2.0";
12513299c2f1SGregory Neil Shapiro #ifdef EDQUOT
1252b4662009SGregory Neil Shapiro 		if (errno == EDQUOT && BounceQuota)
1253b4662009SGregory Neil Shapiro 			errcode = "552 5.2.2";
12543299c2f1SGregory Neil Shapiro #endif /* EDQUOT */
125512ed1c7cSGregory Neil Shapiro 		mailerr(errcode, "%s: %s", path, sm_errstring(errno));
12567660b554SGregory Neil Shapiro 		mbfd = open(path, O_WRONLY, 0);
12577660b554SGregory Neil Shapiro 		if (mbfd < 0 ||
12587660b554SGregory Neil Shapiro 		    cursize == 0
12597660b554SGregory Neil Shapiro 		    || flock(mbfd, LOCK_EX) < 0 ||
12607660b554SGregory Neil Shapiro 		    fstat(mbfd, &sb) < 0 ||
12617660b554SGregory Neil Shapiro 		    sb.st_size != cursize ||
12622ef40764SGregory Neil Shapiro 		    sb.st_nlink != 1 ||
12632ef40764SGregory Neil Shapiro 		    !S_ISREG(sb.st_mode) ||
12642ef40764SGregory Neil Shapiro 		    sb.st_dev != fsb.st_dev ||
12652ef40764SGregory Neil Shapiro 		    sb.st_ino != fsb.st_ino ||
12662ef40764SGregory Neil Shapiro # if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
12672ef40764SGregory Neil Shapiro 		    sb.st_gen != fsb.st_gen ||
12682ef40764SGregory Neil Shapiro # endif /* HAS_ST_GEN && 0 */
12692ef40764SGregory Neil Shapiro 		    sb.st_uid != fsb.st_uid
12702ef40764SGregory Neil Shapiro 		   )
12712ef40764SGregory Neil Shapiro 		{
12722ef40764SGregory Neil Shapiro 			/* Don't use a bogus file */
12732ef40764SGregory Neil Shapiro 			if (mbfd >= 0)
12742ef40764SGregory Neil Shapiro 			{
12752ef40764SGregory Neil Shapiro 				(void) close(mbfd);
12762ef40764SGregory Neil Shapiro 				mbfd = -1;
12772ef40764SGregory Neil Shapiro 			}
12782ef40764SGregory Neil Shapiro 		}
12792ef40764SGregory Neil Shapiro 
12802ef40764SGregory Neil Shapiro 		/* Attempt to truncate back to pre-write size */
12812ef40764SGregory Neil Shapiro 		goto err3;
12823299c2f1SGregory Neil Shapiro 	}
12833299c2f1SGregory Neil Shapiro 	else if (!nobiff)
1284c2aa98e2SPeter Wemm 		notifybiff(biffmsg);
1285c2aa98e2SPeter Wemm 
12863299c2f1SGregory Neil Shapiro 	if (setreuid(0, 0) < 0)
12873299c2f1SGregory Neil Shapiro 	{
1288c2aa98e2SPeter Wemm 		mailerr("450 4.2.0", "setreuid(0, 0): %s",
128912ed1c7cSGregory Neil Shapiro 			sm_errstring(errno));
1290c2aa98e2SPeter Wemm 		goto err0;
1291c2aa98e2SPeter Wemm 	}
1292c2aa98e2SPeter Wemm #ifdef DEBUG
129312ed1c7cSGregory Neil Shapiro 	fprintf(stderr, "reset euid = %d\n", (int) geteuid());
12943299c2f1SGregory Neil Shapiro #endif /* DEBUG */
1295c2aa98e2SPeter Wemm 	unlockmbox();
12963299c2f1SGregory Neil Shapiro 	if (LMTPMode)
1297b4662009SGregory Neil Shapiro 		printf("250 2.1.5 %s Ok\r\n", name);
1298c2aa98e2SPeter Wemm }
1299c2aa98e2SPeter Wemm 
1300c2aa98e2SPeter Wemm /*
13013299c2f1SGregory Neil Shapiro **  user.lock files are necessary for compatibility with other
13023299c2f1SGregory Neil Shapiro **  systems, e.g., when the mail spool file is NFS exported.
13033299c2f1SGregory Neil Shapiro **  Alas, mailbox locking is more than just a local matter.
13043299c2f1SGregory Neil Shapiro **  EPA 11/94.
1305c2aa98e2SPeter Wemm */
1306c2aa98e2SPeter Wemm 
130712ed1c7cSGregory Neil Shapiro bool	Locked = false;
13083299c2f1SGregory Neil Shapiro 
13093299c2f1SGregory Neil Shapiro #ifdef MAILLOCK
13103299c2f1SGregory Neil Shapiro int
13113299c2f1SGregory Neil Shapiro lockmbox(name)
13123299c2f1SGregory Neil Shapiro 	char *name;
13133299c2f1SGregory Neil Shapiro {
1314d995d2baSGregory Neil Shapiro 	int r = 0;
13153299c2f1SGregory Neil Shapiro 
13163299c2f1SGregory Neil Shapiro 	if (Locked)
13173299c2f1SGregory Neil Shapiro 		return 0;
13183299c2f1SGregory Neil Shapiro 	if ((r = maillock(name, 15)) == L_SUCCESS)
13193299c2f1SGregory Neil Shapiro 	{
132012ed1c7cSGregory Neil Shapiro 		Locked = true;
13213299c2f1SGregory Neil Shapiro 		return 0;
13223299c2f1SGregory Neil Shapiro 	}
13233299c2f1SGregory Neil Shapiro 	switch (r)
13243299c2f1SGregory Neil Shapiro 	{
13253299c2f1SGregory Neil Shapiro 	  case L_TMPLOCK:	/* Can't create tmp file */
13263299c2f1SGregory Neil Shapiro 	  case L_TMPWRITE:	/* Can't write pid into lockfile */
13273299c2f1SGregory Neil Shapiro 	  case L_MAXTRYS:	/* Failed after retrycnt attempts */
13283299c2f1SGregory Neil Shapiro 		errno = 0;
13293299c2f1SGregory Neil Shapiro 		r = EX_TEMPFAIL;
13303299c2f1SGregory Neil Shapiro 		break;
13313299c2f1SGregory Neil Shapiro 	  case L_ERROR:		/* Check errno for reason */
13323299c2f1SGregory Neil Shapiro 		r = errno;
13333299c2f1SGregory Neil Shapiro 		break;
13343299c2f1SGregory Neil Shapiro 	  default:		/* other permanent errors */
13353299c2f1SGregory Neil Shapiro 		errno = 0;
13363299c2f1SGregory Neil Shapiro 		r = EX_UNAVAILABLE;
13373299c2f1SGregory Neil Shapiro 		break;
13383299c2f1SGregory Neil Shapiro 	}
13393299c2f1SGregory Neil Shapiro 	return r;
13403299c2f1SGregory Neil Shapiro }
1341c2aa98e2SPeter Wemm 
1342c2aa98e2SPeter Wemm void
13433299c2f1SGregory Neil Shapiro unlockmbox()
13443299c2f1SGregory Neil Shapiro {
13453299c2f1SGregory Neil Shapiro 	if (Locked)
13463299c2f1SGregory Neil Shapiro 		mailunlock();
134712ed1c7cSGregory Neil Shapiro 	Locked = false;
13483299c2f1SGregory Neil Shapiro }
13493299c2f1SGregory Neil Shapiro #else /* MAILLOCK */
13503299c2f1SGregory Neil Shapiro 
13513299c2f1SGregory Neil Shapiro char	LockName[MAXPATHLEN];
13523299c2f1SGregory Neil Shapiro 
13533299c2f1SGregory Neil Shapiro int
1354c2aa98e2SPeter Wemm lockmbox(path)
1355c2aa98e2SPeter Wemm 	char *path;
1356c2aa98e2SPeter Wemm {
1357c2aa98e2SPeter Wemm 	int statfailed = 0;
13583299c2f1SGregory Neil Shapiro 	time_t start;
1359c2aa98e2SPeter Wemm 
13603299c2f1SGregory Neil Shapiro 	if (Locked)
13613299c2f1SGregory Neil Shapiro 		return 0;
13623299c2f1SGregory Neil Shapiro 	if (strlen(path) + 6 > sizeof LockName)
13633299c2f1SGregory Neil Shapiro 		return EX_SOFTWARE;
136412ed1c7cSGregory Neil Shapiro 	(void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path);
13653299c2f1SGregory Neil Shapiro 	(void) time(&start);
13663299c2f1SGregory Neil Shapiro 	for (; ; sleep(5))
13673299c2f1SGregory Neil Shapiro 	{
1368c2aa98e2SPeter Wemm 		int fd;
1369c2aa98e2SPeter Wemm 		struct stat st;
1370c2aa98e2SPeter Wemm 		time_t now;
1371c2aa98e2SPeter Wemm 
13723299c2f1SGregory Neil Shapiro 		/* global timeout */
13733299c2f1SGregory Neil Shapiro 		(void) time(&now);
13743299c2f1SGregory Neil Shapiro 		if (now > start + LOCKTO_GLOB)
13753299c2f1SGregory Neil Shapiro 		{
13763299c2f1SGregory Neil Shapiro 			errno = 0;
13773299c2f1SGregory Neil Shapiro 			return EX_TEMPFAIL;
1378c2aa98e2SPeter Wemm 		}
13797660b554SGregory Neil Shapiro 		fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, LOCKFILE_PMODE);
13803299c2f1SGregory Neil Shapiro 		if (fd >= 0)
13813299c2f1SGregory Neil Shapiro 		{
13823299c2f1SGregory Neil Shapiro 			/* defeat lock checking programs which test pid */
13833299c2f1SGregory Neil Shapiro 			(void) write(fd, "0", 2);
138412ed1c7cSGregory Neil Shapiro 			Locked = true;
13853299c2f1SGregory Neil Shapiro 			(void) close(fd);
13863299c2f1SGregory Neil Shapiro 			return 0;
13873299c2f1SGregory Neil Shapiro 		}
13883299c2f1SGregory Neil Shapiro 		if (stat(LockName, &st) < 0)
13893299c2f1SGregory Neil Shapiro 		{
1390c2aa98e2SPeter Wemm 			if (statfailed++ > 5)
13913299c2f1SGregory Neil Shapiro 			{
13923299c2f1SGregory Neil Shapiro 				errno = 0;
13933299c2f1SGregory Neil Shapiro 				return EX_TEMPFAIL;
13943299c2f1SGregory Neil Shapiro 			}
1395c2aa98e2SPeter Wemm 			continue;
1396c2aa98e2SPeter Wemm 		}
1397c2aa98e2SPeter Wemm 		statfailed = 0;
13983299c2f1SGregory Neil Shapiro 		(void) time(&now);
13993299c2f1SGregory Neil Shapiro 		if (now < st.st_ctime + LOCKTO_RM)
1400c2aa98e2SPeter Wemm 			continue;
14013299c2f1SGregory Neil Shapiro 
14023299c2f1SGregory Neil Shapiro 		/* try to remove stale lockfile */
14033299c2f1SGregory Neil Shapiro 		if (unlink(LockName) < 0)
14043299c2f1SGregory Neil Shapiro 			return errno;
1405c2aa98e2SPeter Wemm 	}
1406c2aa98e2SPeter Wemm }
1407c2aa98e2SPeter Wemm 
1408c2aa98e2SPeter Wemm void
1409c2aa98e2SPeter Wemm unlockmbox()
1410c2aa98e2SPeter Wemm {
14113299c2f1SGregory Neil Shapiro 	if (!Locked)
1412c2aa98e2SPeter Wemm 		return;
14133299c2f1SGregory Neil Shapiro 	(void) unlink(LockName);
141412ed1c7cSGregory Neil Shapiro 	Locked = false;
1415c2aa98e2SPeter Wemm }
14163299c2f1SGregory Neil Shapiro #endif /* MAILLOCK */
1417c2aa98e2SPeter Wemm 
1418c2aa98e2SPeter Wemm void
1419c2aa98e2SPeter Wemm notifybiff(msg)
1420c2aa98e2SPeter Wemm 	char *msg;
1421c2aa98e2SPeter Wemm {
142212ed1c7cSGregory Neil Shapiro 	static bool initialized = false;
1423c2aa98e2SPeter Wemm 	static int f = -1;
1424c2aa98e2SPeter Wemm 	struct hostent *hp;
1425c2aa98e2SPeter Wemm 	struct servent *sp;
1426c2aa98e2SPeter Wemm 	int len;
14273299c2f1SGregory Neil Shapiro 	static struct sockaddr_in addr;
1428c2aa98e2SPeter Wemm 
14293299c2f1SGregory Neil Shapiro 	if (!initialized)
14303299c2f1SGregory Neil Shapiro 	{
143112ed1c7cSGregory Neil Shapiro 		initialized = true;
14323299c2f1SGregory Neil Shapiro 
1433c2aa98e2SPeter Wemm 		/* Be silent if biff service not available. */
14343299c2f1SGregory Neil Shapiro 		if ((sp = getservbyname("biff", "udp")) == NULL ||
14353299c2f1SGregory Neil Shapiro 		    (hp = gethostbyname("localhost")) == NULL ||
14363299c2f1SGregory Neil Shapiro 		    hp->h_length != INADDRSZ)
1437c2aa98e2SPeter Wemm 			return;
14383299c2f1SGregory Neil Shapiro 
1439c2aa98e2SPeter Wemm 		addr.sin_family = hp->h_addrtype;
14403299c2f1SGregory Neil Shapiro 		memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ);
1441c2aa98e2SPeter Wemm 		addr.sin_port = sp->s_port;
1442c2aa98e2SPeter Wemm 	}
14433299c2f1SGregory Neil Shapiro 
14443299c2f1SGregory Neil Shapiro 	/* No message, just return */
14453299c2f1SGregory Neil Shapiro 	if (msg == NULL)
1446c2aa98e2SPeter Wemm 		return;
14473299c2f1SGregory Neil Shapiro 
14483299c2f1SGregory Neil Shapiro 	/* Couldn't initialize addr struct */
14493299c2f1SGregory Neil Shapiro 	if (addr.sin_family == AF_UNSPEC)
14503299c2f1SGregory Neil Shapiro 		return;
14513299c2f1SGregory Neil Shapiro 
1452b4662009SGregory Neil Shapiro 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
14533299c2f1SGregory Neil Shapiro 		return;
1454c2aa98e2SPeter Wemm 	len = strlen(msg) + 1;
145576b7bf71SPeter Wemm 	(void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr));
1456c2aa98e2SPeter Wemm }
1457c2aa98e2SPeter Wemm 
1458c2aa98e2SPeter Wemm void
1459c2aa98e2SPeter Wemm usage()
1460c2aa98e2SPeter Wemm {
14613299c2f1SGregory Neil Shapiro 	ExitVal = EX_USAGE;
146212ed1c7cSGregory Neil Shapiro 	mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ...");
14633299c2f1SGregory Neil Shapiro 	exit(ExitVal);
1464c2aa98e2SPeter Wemm }
1465c2aa98e2SPeter Wemm 
1466c2aa98e2SPeter Wemm void
146712ed1c7cSGregory Neil Shapiro /*VARARGS2*/
1468c2aa98e2SPeter Wemm #ifdef __STDC__
1469c2aa98e2SPeter Wemm mailerr(const char *hdr, const char *fmt, ...)
14703299c2f1SGregory Neil Shapiro #else /* __STDC__ */
1471c2aa98e2SPeter Wemm mailerr(hdr, fmt, va_alist)
1472c2aa98e2SPeter Wemm 	const char *hdr;
1473c2aa98e2SPeter Wemm 	const char *fmt;
1474c2aa98e2SPeter Wemm 	va_dcl
14753299c2f1SGregory Neil Shapiro #endif /* __STDC__ */
1476c2aa98e2SPeter Wemm {
1477b4662009SGregory Neil Shapiro 	size_t len = 0;
147812ed1c7cSGregory Neil Shapiro 	SM_VA_LOCAL_DECL
1479c2aa98e2SPeter Wemm 
1480b4662009SGregory Neil Shapiro 	(void) e_to_sys(errno);
1481b4662009SGregory Neil Shapiro 
148212ed1c7cSGregory Neil Shapiro 	SM_VA_START(ap, fmt);
1483b4662009SGregory Neil Shapiro 
148412ed1c7cSGregory Neil Shapiro 	if (LMTPMode && hdr != NULL)
1485c2aa98e2SPeter Wemm 	{
148612ed1c7cSGregory Neil Shapiro 		sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr);
1487b4662009SGregory Neil Shapiro 		len = strlen(ErrBuf);
1488c2aa98e2SPeter Wemm 	}
148912ed1c7cSGregory Neil Shapiro 	(void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap);
149012ed1c7cSGregory Neil Shapiro 	SM_VA_END(ap);
1491b4662009SGregory Neil Shapiro 
1492b4662009SGregory Neil Shapiro 	if (!HoldErrs)
1493b4662009SGregory Neil Shapiro 		flush_error();
1494b4662009SGregory Neil Shapiro 
1495b4662009SGregory Neil Shapiro 	/* Log the message to syslog. */
1496b4662009SGregory Neil Shapiro 	if (!LMTPMode)
1497b4662009SGregory Neil Shapiro 		syslog(LOG_ERR, "%s", ErrBuf);
1498b4662009SGregory Neil Shapiro }
1499c2aa98e2SPeter Wemm 
1500c2aa98e2SPeter Wemm void
1501b4662009SGregory Neil Shapiro flush_error()
1502c2aa98e2SPeter Wemm {
1503b4662009SGregory Neil Shapiro 	if (LMTPMode)
1504b4662009SGregory Neil Shapiro 		printf("%s\r\n", ErrBuf);
1505b4662009SGregory Neil Shapiro 	else
1506b4662009SGregory Neil Shapiro 	{
15073299c2f1SGregory Neil Shapiro 		if (ExitVal != EX_USAGE)
1508c2aa98e2SPeter Wemm 			(void) fprintf(stderr, "mail.local: ");
1509b4662009SGregory Neil Shapiro 		fprintf(stderr, "%s\n", ErrBuf);
1510c2aa98e2SPeter Wemm 	}
1511c2aa98e2SPeter Wemm }
1512c2aa98e2SPeter Wemm 
1513c2aa98e2SPeter Wemm /*
1514c2aa98e2SPeter Wemm  * e_to_sys --
1515c2aa98e2SPeter Wemm  *	Guess which errno's are temporary.  Gag me.
1516c2aa98e2SPeter Wemm  */
1517b4662009SGregory Neil Shapiro 
15183299c2f1SGregory Neil Shapiro int
1519c2aa98e2SPeter Wemm e_to_sys(num)
1520c2aa98e2SPeter Wemm 	int num;
1521c2aa98e2SPeter Wemm {
1522c2aa98e2SPeter Wemm 	/* Temporary failures override hard errors. */
15233299c2f1SGregory Neil Shapiro 	if (ExitVal == EX_TEMPFAIL)
15243299c2f1SGregory Neil Shapiro 		return ExitVal;
1525c2aa98e2SPeter Wemm 
15263299c2f1SGregory Neil Shapiro 	switch (num)		/* Hopefully temporary errors. */
15273299c2f1SGregory Neil Shapiro 	{
1528c2aa98e2SPeter Wemm #ifdef EDQUOT
1529c2aa98e2SPeter Wemm 	  case EDQUOT:		/* Disc quota exceeded */
1530b4662009SGregory Neil Shapiro 		if (BounceQuota)
15313299c2f1SGregory Neil Shapiro 		{
15323299c2f1SGregory Neil Shapiro 			ExitVal = EX_UNAVAILABLE;
15333299c2f1SGregory Neil Shapiro 			break;
15343299c2f1SGregory Neil Shapiro 		}
15353299c2f1SGregory Neil Shapiro 		/* FALLTHROUGH */
15363299c2f1SGregory Neil Shapiro #endif /* EDQUOT */
15373299c2f1SGregory Neil Shapiro #ifdef EAGAIN
15383299c2f1SGregory Neil Shapiro 	  case EAGAIN:		/* Resource temporarily unavailable */
15393299c2f1SGregory Neil Shapiro #endif /* EAGAIN */
1540c2aa98e2SPeter Wemm #ifdef EBUSY
1541c2aa98e2SPeter Wemm 	  case EBUSY:		/* Device busy */
15423299c2f1SGregory Neil Shapiro #endif /* EBUSY */
1543c2aa98e2SPeter Wemm #ifdef EPROCLIM
1544c2aa98e2SPeter Wemm 	  case EPROCLIM:	/* Too many processes */
15453299c2f1SGregory Neil Shapiro #endif /* EPROCLIM */
1546c2aa98e2SPeter Wemm #ifdef EUSERS
1547c2aa98e2SPeter Wemm 	  case EUSERS:		/* Too many users */
15483299c2f1SGregory Neil Shapiro #endif /* EUSERS */
1549c2aa98e2SPeter Wemm #ifdef ECONNABORTED
1550c2aa98e2SPeter Wemm 	  case ECONNABORTED:	/* Software caused connection abort */
15513299c2f1SGregory Neil Shapiro #endif /* ECONNABORTED */
1552c2aa98e2SPeter Wemm #ifdef ECONNREFUSED
1553c2aa98e2SPeter Wemm 	  case ECONNREFUSED:	/* Connection refused */
15543299c2f1SGregory Neil Shapiro #endif /* ECONNREFUSED */
1555c2aa98e2SPeter Wemm #ifdef ECONNRESET
1556c2aa98e2SPeter Wemm 	  case ECONNRESET:	/* Connection reset by peer */
15573299c2f1SGregory Neil Shapiro #endif /* ECONNRESET */
1558c2aa98e2SPeter Wemm #ifdef EDEADLK
1559c2aa98e2SPeter Wemm 	  case EDEADLK:		/* Resource deadlock avoided */
15603299c2f1SGregory Neil Shapiro #endif /* EDEADLK */
1561c2aa98e2SPeter Wemm #ifdef EFBIG
1562c2aa98e2SPeter Wemm 	  case EFBIG:		/* File too large */
15633299c2f1SGregory Neil Shapiro #endif /* EFBIG */
1564c2aa98e2SPeter Wemm #ifdef EHOSTDOWN
1565c2aa98e2SPeter Wemm 	  case EHOSTDOWN:	/* Host is down */
15663299c2f1SGregory Neil Shapiro #endif /* EHOSTDOWN */
1567c2aa98e2SPeter Wemm #ifdef EHOSTUNREACH
1568c2aa98e2SPeter Wemm 	  case EHOSTUNREACH:	/* No route to host */
15693299c2f1SGregory Neil Shapiro #endif /* EHOSTUNREACH */
1570c2aa98e2SPeter Wemm #ifdef EMFILE
1571c2aa98e2SPeter Wemm 	  case EMFILE:		/* Too many open files */
15723299c2f1SGregory Neil Shapiro #endif /* EMFILE */
1573c2aa98e2SPeter Wemm #ifdef ENETDOWN
1574c2aa98e2SPeter Wemm 	  case ENETDOWN:	/* Network is down */
15753299c2f1SGregory Neil Shapiro #endif /* ENETDOWN */
1576c2aa98e2SPeter Wemm #ifdef ENETRESET
1577c2aa98e2SPeter Wemm 	  case ENETRESET:	/* Network dropped connection on reset */
15783299c2f1SGregory Neil Shapiro #endif /* ENETRESET */
1579c2aa98e2SPeter Wemm #ifdef ENETUNREACH
1580c2aa98e2SPeter Wemm 	  case ENETUNREACH:	/* Network is unreachable */
15813299c2f1SGregory Neil Shapiro #endif /* ENETUNREACH */
1582c2aa98e2SPeter Wemm #ifdef ENFILE
1583c2aa98e2SPeter Wemm 	  case ENFILE:		/* Too many open files in system */
15843299c2f1SGregory Neil Shapiro #endif /* ENFILE */
1585c2aa98e2SPeter Wemm #ifdef ENOBUFS
1586c2aa98e2SPeter Wemm 	  case ENOBUFS:		/* No buffer space available */
15873299c2f1SGregory Neil Shapiro #endif /* ENOBUFS */
1588c2aa98e2SPeter Wemm #ifdef ENOMEM
1589c2aa98e2SPeter Wemm 	  case ENOMEM:		/* Cannot allocate memory */
15903299c2f1SGregory Neil Shapiro #endif /* ENOMEM */
1591c2aa98e2SPeter Wemm #ifdef ENOSPC
1592c2aa98e2SPeter Wemm 	  case ENOSPC:		/* No space left on device */
15933299c2f1SGregory Neil Shapiro #endif /* ENOSPC */
1594c2aa98e2SPeter Wemm #ifdef EROFS
1595c2aa98e2SPeter Wemm 	  case EROFS:		/* Read-only file system */
15963299c2f1SGregory Neil Shapiro #endif /* EROFS */
1597c2aa98e2SPeter Wemm #ifdef ESTALE
1598c2aa98e2SPeter Wemm 	  case ESTALE:		/* Stale NFS file handle */
15993299c2f1SGregory Neil Shapiro #endif /* ESTALE */
1600c2aa98e2SPeter Wemm #ifdef ETIMEDOUT
1601c2aa98e2SPeter Wemm 	  case ETIMEDOUT:	/* Connection timed out */
16023299c2f1SGregory Neil Shapiro #endif /* ETIMEDOUT */
1603c2aa98e2SPeter Wemm #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
1604c2aa98e2SPeter Wemm 	  case EWOULDBLOCK:	/* Operation would block. */
16053299c2f1SGregory Neil Shapiro #endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */
16063299c2f1SGregory Neil Shapiro 		ExitVal = EX_TEMPFAIL;
1607c2aa98e2SPeter Wemm 		break;
16083299c2f1SGregory Neil Shapiro 
1609c2aa98e2SPeter Wemm 	  default:
16103299c2f1SGregory Neil Shapiro 		ExitVal = EX_UNAVAILABLE;
1611c2aa98e2SPeter Wemm 		break;
1612c2aa98e2SPeter Wemm 	}
16133299c2f1SGregory Neil Shapiro 	return ExitVal;
1614c2aa98e2SPeter Wemm }
1615c2aa98e2SPeter Wemm 
1616c2aa98e2SPeter Wemm #if defined(ultrix) || defined(_CRAY)
1617c2aa98e2SPeter Wemm /*
1618c2aa98e2SPeter Wemm  * Copyright (c) 1987, 1993
1619c2aa98e2SPeter Wemm  *	The Regents of the University of California.  All rights reserved.
1620c2aa98e2SPeter Wemm  *
1621c2aa98e2SPeter Wemm  * Redistribution and use in source and binary forms, with or without
1622c2aa98e2SPeter Wemm  * modification, are permitted provided that the following conditions
1623c2aa98e2SPeter Wemm  * are met:
1624c2aa98e2SPeter Wemm  * 1. Redistributions of source code must retain the above copyright
1625c2aa98e2SPeter Wemm  *    notice, this list of conditions and the following disclaimer.
1626c2aa98e2SPeter Wemm  * 2. Redistributions in binary form must reproduce the above copyright
1627c2aa98e2SPeter Wemm  *    notice, this list of conditions and the following disclaimer in the
1628c2aa98e2SPeter Wemm  *    documentation and/or other materials provided with the distribution.
1629c2aa98e2SPeter Wemm  * 3. All advertising materials mentioning features or use of this software
1630c2aa98e2SPeter Wemm  *    must display the following acknowledgement:
1631c2aa98e2SPeter Wemm  *	This product includes software developed by the University of
1632c2aa98e2SPeter Wemm  *	California, Berkeley and its contributors.
1633c2aa98e2SPeter Wemm  * 4. Neither the name of the University nor the names of its contributors
1634c2aa98e2SPeter Wemm  *    may be used to endorse or promote products derived from this software
1635c2aa98e2SPeter Wemm  *    without specific prior written permission.
1636c2aa98e2SPeter Wemm  *
1637c2aa98e2SPeter Wemm  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1638c2aa98e2SPeter Wemm  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1639c2aa98e2SPeter Wemm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1640c2aa98e2SPeter Wemm  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1641c2aa98e2SPeter Wemm  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1642c2aa98e2SPeter Wemm  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1643c2aa98e2SPeter Wemm  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1644c2aa98e2SPeter Wemm  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1645c2aa98e2SPeter Wemm  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1646c2aa98e2SPeter Wemm  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1647c2aa98e2SPeter Wemm  * SUCH DAMAGE.
1648c2aa98e2SPeter Wemm  */
1649c2aa98e2SPeter Wemm 
1650c2aa98e2SPeter Wemm # if defined(LIBC_SCCS) && !defined(lint)
1651c2aa98e2SPeter Wemm static char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
16523299c2f1SGregory Neil Shapiro # endif /* defined(LIBC_SCCS) && !defined(lint) */
1653c2aa98e2SPeter Wemm 
1654c2aa98e2SPeter Wemm # include <sys/types.h>
1655c2aa98e2SPeter Wemm # include <sys/stat.h>
1656c2aa98e2SPeter Wemm # include <fcntl.h>
1657c2aa98e2SPeter Wemm # include <errno.h>
1658c2aa98e2SPeter Wemm # include <stdio.h>
1659c2aa98e2SPeter Wemm # include <ctype.h>
1660c2aa98e2SPeter Wemm 
1661c2aa98e2SPeter Wemm static int _gettemp();
1662c2aa98e2SPeter Wemm 
1663c2aa98e2SPeter Wemm mkstemp(path)
1664c2aa98e2SPeter Wemm 	char *path;
1665c2aa98e2SPeter Wemm {
1666c2aa98e2SPeter Wemm 	int fd;
1667c2aa98e2SPeter Wemm 
1668c2aa98e2SPeter Wemm 	return (_gettemp(path, &fd) ? fd : -1);
1669c2aa98e2SPeter Wemm }
1670c2aa98e2SPeter Wemm 
1671c2aa98e2SPeter Wemm static
1672c2aa98e2SPeter Wemm _gettemp(path, doopen)
1673c2aa98e2SPeter Wemm 	char *path;
1674c2aa98e2SPeter Wemm 	register int *doopen;
1675c2aa98e2SPeter Wemm {
1676c2aa98e2SPeter Wemm 	extern int errno;
1677c2aa98e2SPeter Wemm 	register char *start, *trv;
1678c2aa98e2SPeter Wemm 	struct stat sbuf;
167912ed1c7cSGregory Neil Shapiro 	unsigned int pid;
1680c2aa98e2SPeter Wemm 
1681c2aa98e2SPeter Wemm 	pid = getpid();
1682c2aa98e2SPeter Wemm 	for (trv = path; *trv; ++trv);		/* extra X's get set to 0's */
16833299c2f1SGregory Neil Shapiro 	while (*--trv == 'X')
16843299c2f1SGregory Neil Shapiro 	{
1685c2aa98e2SPeter Wemm 		*trv = (pid % 10) + '0';
1686c2aa98e2SPeter Wemm 		pid /= 10;
1687c2aa98e2SPeter Wemm 	}
1688c2aa98e2SPeter Wemm 
1689c2aa98e2SPeter Wemm 	/*
1690c2aa98e2SPeter Wemm 	 * check the target directory; if you have six X's and it
1691c2aa98e2SPeter Wemm 	 * doesn't exist this runs for a *very* long time.
1692c2aa98e2SPeter Wemm 	 */
16933299c2f1SGregory Neil Shapiro 	for (start = trv + 1;; --trv)
16943299c2f1SGregory Neil Shapiro 	{
1695c2aa98e2SPeter Wemm 		if (trv <= path)
1696c2aa98e2SPeter Wemm 			break;
16973299c2f1SGregory Neil Shapiro 		if (*trv == '/')
16983299c2f1SGregory Neil Shapiro 		{
1699c2aa98e2SPeter Wemm 			*trv = '\0';
1700c2aa98e2SPeter Wemm 			if (stat(path, &sbuf) < 0)
1701c2aa98e2SPeter Wemm 				return(0);
17023299c2f1SGregory Neil Shapiro 			if (!S_ISDIR(sbuf.st_mode))
17033299c2f1SGregory Neil Shapiro 			{
1704c2aa98e2SPeter Wemm 				errno = ENOTDIR;
1705c2aa98e2SPeter Wemm 				return(0);
1706c2aa98e2SPeter Wemm 			}
1707c2aa98e2SPeter Wemm 			*trv = '/';
1708c2aa98e2SPeter Wemm 			break;
1709c2aa98e2SPeter Wemm 		}
1710c2aa98e2SPeter Wemm 	}
1711c2aa98e2SPeter Wemm 
17123299c2f1SGregory Neil Shapiro 	for (;;)
17133299c2f1SGregory Neil Shapiro 	{
17143299c2f1SGregory Neil Shapiro 		if (doopen)
17153299c2f1SGregory Neil Shapiro 		{
1716d995d2baSGregory Neil Shapiro 			if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR,
1717d995d2baSGregory Neil Shapiro 					    0600)) >= 0)
1718c2aa98e2SPeter Wemm 				return(1);
1719c2aa98e2SPeter Wemm 			if (errno != EEXIST)
1720c2aa98e2SPeter Wemm 				return(0);
1721c2aa98e2SPeter Wemm 		}
1722c2aa98e2SPeter Wemm 		else if (stat(path, &sbuf) < 0)
1723c2aa98e2SPeter Wemm 			return(errno == ENOENT ? 1 : 0);
1724c2aa98e2SPeter Wemm 
1725c2aa98e2SPeter Wemm 		/* tricky little algorithm for backward compatibility */
17263299c2f1SGregory Neil Shapiro 		for (trv = start;;)
17273299c2f1SGregory Neil Shapiro 		{
1728c2aa98e2SPeter Wemm 			if (!*trv)
1729c2aa98e2SPeter Wemm 				return(0);
1730c2aa98e2SPeter Wemm 			if (*trv == 'z')
1731c2aa98e2SPeter Wemm 				*trv++ = 'a';
17323299c2f1SGregory Neil Shapiro 			else
17333299c2f1SGregory Neil Shapiro 			{
1734c2aa98e2SPeter Wemm 				if (isascii(*trv) && isdigit(*trv))
1735c2aa98e2SPeter Wemm 					*trv = 'a';
1736c2aa98e2SPeter Wemm 				else
1737c2aa98e2SPeter Wemm 					++*trv;
1738c2aa98e2SPeter Wemm 				break;
1739c2aa98e2SPeter Wemm 			}
1740c2aa98e2SPeter Wemm 		}
1741c2aa98e2SPeter Wemm 	}
1742c2aa98e2SPeter Wemm 	/* NOTREACHED */
1743c2aa98e2SPeter Wemm }
17443299c2f1SGregory Neil Shapiro #endif /* defined(ultrix) || defined(_CRAY) */
1745