mail.local.c revision 98125
142580Speter/*
294337Sgshapiro * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
364565Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1990, 1993, 1994
538032Speter *	The Regents of the University of California.  All rights reserved.
638032Speter *
738032Speter * By using this file, you agree to the terms and conditions set
838032Speter * forth in the LICENSE file which can be found at the top level of
938032Speter * the sendmail distribution.
1038032Speter *
1138032Speter */
1238032Speter
1390795Sgshapiro#include <sm/gen.h>
1490795Sgshapiro
1590795SgshapiroSM_IDSTR(copyright,
1673191Sgshapiro"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
1764565Sgshapiro	All rights reserved.\n\
1864565Sgshapiro     Copyright (c) 1990, 1993, 1994\n\
1990795Sgshapiro	The Regents of the University of California.  All rights reserved.\n")
2038032Speter
2198125SgshapiroSM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.239 2002/05/24 20:56:32 gshapiro Exp $")
2238032Speter
2390795Sgshapiro#include <stdlib.h>
2490795Sgshapiro#include <sm/errstring.h>
2590795Sgshapiro#include <sm/io.h>
2690795Sgshapiro#include <sm/limits.h>
2790795Sgshapiro# include <unistd.h>
2890795Sgshapiro# ifdef EX_OK
2990795Sgshapiro#  undef EX_OK		/* unistd.h may have another use for this */
3090795Sgshapiro# endif /* EX_OK */
3190795Sgshapiro#include <sm/mbdb.h>
3290795Sgshapiro#include <sm/sysexits.h>
3390795Sgshapiro
3464565Sgshapiro/* $FreeBSD: head/contrib/sendmail/mail.local/mail.local.c 98125 2002-06-11 21:16:51Z gshapiro $ */
3564565Sgshapiro
3638032Speter/*
3764565Sgshapiro**  This is not intended to work on System V derived systems
3864565Sgshapiro**  such as Solaris or HP-UX, since they use a totally different
3990795Sgshapiro**  approach to mailboxes (essentially, they have a set-group-ID program
4090795Sgshapiro**  rather than set-user-ID, and they rely on the ability to "give away"
4164565Sgshapiro**  files to do their work).  IT IS NOT A BUG that this doesn't
4264565Sgshapiro**  work on such architectures.
4364565Sgshapiro*/
4438032Speter
4564565Sgshapiro
4690795Sgshapiro#include <stdio.h>
4790795Sgshapiro#include <errno.h>
4890795Sgshapiro#include <fcntl.h>
4990795Sgshapiro#include <sys/types.h>
5090795Sgshapiro#include <sys/stat.h>
5190795Sgshapiro#include <time.h>
5290795Sgshapiro#include <stdlib.h>
5366497Sgshapiro# include <sys/socket.h>
5466497Sgshapiro# include <sys/file.h>
5566497Sgshapiro# include <netinet/in.h>
5666497Sgshapiro# include <arpa/nameser.h>
5766497Sgshapiro# include <netdb.h>
5890795Sgshapiro# include <pwd.h>
5964565Sgshapiro
6090795Sgshapiro#include <sm/string.h>
6190795Sgshapiro#include <syslog.h>
6290795Sgshapiro#include <ctype.h>
6366497Sgshapiro
6490795Sgshapiro#include <sm/conf.h>
6590795Sgshapiro#include <sendmail/pathnames.h>
6664565Sgshapiro
6764565Sgshapiro
6890795Sgshapiro/* additional mode for open() */
6990795Sgshapiro# define EXTRA_MODE 0
7064565Sgshapiro
7164565Sgshapiro
7290795Sgshapiro#ifndef LOCKTO_RM
7390795Sgshapiro# define LOCKTO_RM	300	/* timeout for stale lockfile removal */
7490795Sgshapiro#endif /* ! LOCKTO_RM */
7590795Sgshapiro#ifndef LOCKTO_GLOB
7690795Sgshapiro# define LOCKTO_GLOB	400	/* global timeout for lockfile creation */
7790795Sgshapiro#endif /* ! LOCKTO_GLOB */
7890795Sgshapiro
7964565Sgshapiro/* define a realloc() which works for NULL pointers */
8090795Sgshapiro#define REALLOC(ptr, size)	(((ptr) == NULL) ? malloc(size) : realloc(ptr, size))
8138032Speter
8238032Speter/*
8390795Sgshapiro**  If you don't have flock, you could try using lockf instead.
8490795Sgshapiro*/
8538032Speter
8690795Sgshapiro#ifdef LDA_USE_LOCKF
8790795Sgshapiro# define flock(a, b)	lockf(a, b, 0)
8890795Sgshapiro# ifdef LOCK_EX
8990795Sgshapiro#  undef LOCK_EX
9090795Sgshapiro# endif /* LOCK_EX */
9190795Sgshapiro# define LOCK_EX	F_LOCK
9290795Sgshapiro#endif /* LDA_USE_LOCKF */
9338032Speter
9490795Sgshapiro#ifndef LOCK_EX
9590795Sgshapiro# include <sys/file.h>
9690795Sgshapiro#endif /* ! LOCK_EX */
9738032Speter
9838032Speter/*
9964565Sgshapiro**  If you don't have setreuid, and you have saved uids, and you have
10064565Sgshapiro**  a seteuid() call that doesn't try to emulate using setuid(), then
10190795Sgshapiro**  you can try defining LDA_USE_SETEUID.
10264565Sgshapiro*/
10373191Sgshapiro
10490795Sgshapiro#ifdef LDA_USE_SETEUID
10590795Sgshapiro# define setreuid(r, e)		seteuid(e)
10690795Sgshapiro#endif /* LDA_USE_SETEUID */
10738032Speter
10890795Sgshapiro#ifdef LDA_CONTENTLENGTH
10990795Sgshapiro# define CONTENTLENGTH	1
11090795Sgshapiro#endif /* LDA_CONTENTLENGTH */
11173191Sgshapiro
11264565Sgshapiro#ifndef INADDRSZ
11364565Sgshapiro# define INADDRSZ	4		/* size of an IPv4 address in bytes */
11464565Sgshapiro#endif /* ! INADDRSZ */
11564565Sgshapiro
11690795Sgshapiro#ifdef MAILLOCK
11790795Sgshapiro# include <maillock.h>
11890795Sgshapiro#endif /* MAILLOCK */
11990795Sgshapiro
12042580Speter#ifndef MAILER_DAEMON
12142580Speter# define MAILER_DAEMON	"MAILER-DAEMON"
12264565Sgshapiro#endif /* ! MAILER_DAEMON */
12342580Speter
12464565Sgshapiro#ifdef CONTENTLENGTH
12564565Sgshapirochar	ContentHdr[40] = "Content-Length: ";
12664565Sgshapirooff_t	HeaderLength;
12764565Sgshapirooff_t	BodyLength;
12864565Sgshapiro#endif /* CONTENTLENGTH */
12938032Speter
13090795Sgshapirobool	EightBitMime = true;		/* advertise 8BITMIME in LMTP */
13173191Sgshapirochar	ErrBuf[10240];			/* error buffer */
13264565Sgshapiroint	ExitVal = EX_OK;		/* sysexits.h error value. */
13390795Sgshapirobool	nobiff = false;
13490795Sgshapirobool	nofsync = false;
13590795Sgshapirobool	HoldErrs = false;		/* Hold errors in ErrBuf */
13690795Sgshapirobool	LMTPMode = false;
13790795Sgshapirobool	BounceQuota = false;		/* permanent error when over quota */
13890795Sgshapirochar	*HomeMailFile = NULL;		/* store mail in homedir */
13938032Speter
14073191Sgshapirovoid	deliver __P((int, char *));
14164565Sgshapiroint	e_to_sys __P((int));
14264565Sgshapirovoid	notifybiff __P((char *));
14390795Sgshapiroint	store __P((char *, bool *));
14464565Sgshapirovoid	usage __P((void));
14564565Sgshapiroint	lockmbox __P((char *));
14664565Sgshapirovoid	unlockmbox __P((void));
14764565Sgshapirovoid	mailerr __P((const char *, const char *, ...));
14873191Sgshapirovoid	flush_error __P((void));
14964565Sgshapiro
15064565Sgshapiro
15138032Speterint
15238032Spetermain(argc, argv)
15338032Speter	int argc;
15438032Speter	char *argv[];
15538032Speter{
15638032Speter	struct passwd *pw;
15764565Sgshapiro	int ch, fd;
15838032Speter	uid_t uid;
15938032Speter	char *from;
16090795Sgshapiro	char *mbdbname = "pw";
16190795Sgshapiro	int err;
16238032Speter	extern char *optarg;
16338032Speter	extern int optind;
16438032Speter
16564565Sgshapiro
16638032Speter	/* make sure we have some open file descriptors */
16738032Speter	for (fd = 10; fd < 30; fd++)
16838032Speter		(void) close(fd);
16938032Speter
17038032Speter	/* use a reasonable umask */
17138032Speter	(void) umask(0077);
17238032Speter
17364565Sgshapiro# ifdef LOG_MAIL
17438032Speter	openlog("mail.local", 0, LOG_MAIL);
17564565Sgshapiro# else /* LOG_MAIL */
17638032Speter	openlog("mail.local", 0);
17764565Sgshapiro# endif /* LOG_MAIL */
17838032Speter
17938032Speter	from = NULL;
18090795Sgshapiro	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:ls")) != -1)
18164565Sgshapiro	{
18264565Sgshapiro		switch(ch)
18364565Sgshapiro		{
18464565Sgshapiro		  case '7':		/* Do not advertise 8BITMIME */
18590795Sgshapiro			EightBitMime = false;
18638084Speter			break;
18764565Sgshapiro
18864565Sgshapiro		  case 'B':
18990825Sgshapiro			nobiff = true;
19038032Speter			break;
19164565Sgshapiro
19264565Sgshapiro		  case 'b':		/* bounce mail when over quota. */
19390795Sgshapiro			BounceQuota = true;
19464565Sgshapiro			break;
19564565Sgshapiro
19664565Sgshapiro		  case 'd':		/* Backward compatible. */
19764565Sgshapiro			break;
19864565Sgshapiro
19990795Sgshapiro		  case 'D':		/* mailbox database type */
20090795Sgshapiro			mbdbname = optarg;
20190795Sgshapiro			break;
20290795Sgshapiro
20364565Sgshapiro		  case 'f':
20464565Sgshapiro		  case 'r':		/* Backward compatible. */
20564565Sgshapiro			if (from != NULL)
20664565Sgshapiro			{
20773191Sgshapiro				mailerr(NULL, "Multiple -f options");
20838032Speter				usage();
20938032Speter			}
21038032Speter			from = optarg;
21138032Speter			break;
21264565Sgshapiro
21390795Sgshapiro		  case 'h':
21490795Sgshapiro			if (optarg != NULL || *optarg != '\0')
21590795Sgshapiro				HomeMailFile = optarg;
21690795Sgshapiro			else
21790795Sgshapiro			{
21890795Sgshapiro				mailerr(NULL, "-h: missing filename");
21990795Sgshapiro				usage();
22090795Sgshapiro			}
22190795Sgshapiro			break;
22290795Sgshapiro
22364565Sgshapiro		  case 'l':
22490795Sgshapiro			LMTPMode = true;
22538032Speter			break;
22664565Sgshapiro
22764565Sgshapiro		  case 's':
22838089Speter			nofsync++;
22938084Speter			break;
23064565Sgshapiro
23164565Sgshapiro		  case '?':
23264565Sgshapiro		  default:
23338032Speter			usage();
23438032Speter		}
23564565Sgshapiro	}
23638032Speter	argc -= optind;
23738032Speter	argv += optind;
23838032Speter
23964565Sgshapiro	/* initialize biff structures */
24064565Sgshapiro	if (!nobiff)
24164565Sgshapiro		notifybiff(NULL);
24238032Speter
24390795Sgshapiro	err = sm_mbdb_initialize(mbdbname);
24490795Sgshapiro	if (err != EX_OK)
24590795Sgshapiro	{
24690795Sgshapiro		char *errcode = "521";
24790795Sgshapiro
24890795Sgshapiro		if (err == EX_TEMPFAIL)
24990795Sgshapiro			errcode = "421";
25090795Sgshapiro
25190795Sgshapiro		mailerr(errcode, "Can not open mailbox database %s: %s",
25290795Sgshapiro			mbdbname, sm_strexit(err));
25390795Sgshapiro		exit(err);
25490795Sgshapiro	}
25590795Sgshapiro
25664565Sgshapiro	if (LMTPMode)
25773191Sgshapiro	{
25873191Sgshapiro		extern void dolmtp __P((void));
25964565Sgshapiro
26073191Sgshapiro		if (argc > 0)
26173191Sgshapiro		{
26273191Sgshapiro			mailerr("421", "Users should not be specified in command line if LMTP required");
26373191Sgshapiro			exit(EX_TEMPFAIL);
26473191Sgshapiro		}
26573191Sgshapiro
26673191Sgshapiro		dolmtp();
26773191Sgshapiro		/* NOTREACHED */
26873191Sgshapiro		exit(EX_OK);
26973191Sgshapiro	}
27073191Sgshapiro
27173191Sgshapiro	/* Non-LMTP from here on out */
27264565Sgshapiro	if (*argv == '\0')
27338032Speter		usage();
27438032Speter
27538032Speter	/*
27664565Sgshapiro	**  If from not specified, use the name from getlogin() if the
27764565Sgshapiro	**  uid matches, otherwise, use the name from the password file
27864565Sgshapiro	**  corresponding to the uid.
27964565Sgshapiro	*/
28073191Sgshapiro
28138032Speter	uid = getuid();
28264565Sgshapiro	if (from == NULL && ((from = getlogin()) == NULL ||
28364565Sgshapiro			     (pw = getpwnam(from)) == NULL ||
28464565Sgshapiro			     pw->pw_uid != uid))
28564565Sgshapiro		from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???";
28664565Sgshapiro
28738032Speter	/*
28864565Sgshapiro	**  There is no way to distinguish the error status of one delivery
28964565Sgshapiro	**  from the rest of the deliveries.  So, if we failed hard on one
29064565Sgshapiro	**  or more deliveries, but had no failures on any of the others, we
29164565Sgshapiro	**  return a hard failure.  If we failed temporarily on one or more
29264565Sgshapiro	**  deliveries, we return a temporary failure regardless of the other
29364565Sgshapiro	**  failures.  This results in the delivery being reattempted later
29464565Sgshapiro	**  at the expense of repeated failures and multiple deliveries.
29564565Sgshapiro	*/
29673191Sgshapiro
29790795Sgshapiro	HoldErrs = true;
29890795Sgshapiro	fd = store(from, NULL);
29990795Sgshapiro	HoldErrs = false;
30073191Sgshapiro	if (fd < 0)
30173191Sgshapiro	{
30273191Sgshapiro		flush_error();
30373191Sgshapiro		exit(ExitVal);
30473191Sgshapiro	}
30573191Sgshapiro	for (; *argv != NULL; ++argv)
30673191Sgshapiro		deliver(fd, *argv);
30764565Sgshapiro	exit(ExitVal);
30864565Sgshapiro	/* NOTREACHED */
30964565Sgshapiro	return ExitVal;
31038032Speter}
31138032Speter
31238032Speterchar *
31364565Sgshapiroparseaddr(s, rcpt)
31438032Speter	char *s;
31564565Sgshapiro	bool rcpt;
31638032Speter{
31738032Speter	char *p;
31864565Sgshapiro	int l;
31938032Speter
32038032Speter	if (*s++ != '<')
32138032Speter		return NULL;
32238032Speter
32338032Speter	p = s;
32438032Speter
32538032Speter	/* at-domain-list */
32664565Sgshapiro	while (*p == '@')
32764565Sgshapiro	{
32838032Speter		p++;
32964565Sgshapiro		while (*p != ',' && *p != ':' && *p != '\0')
33038032Speter			p++;
33164565Sgshapiro		if (*p == '\0')
33238032Speter			return NULL;
33364565Sgshapiro
33464565Sgshapiro		/* Skip over , or : */
33564565Sgshapiro		p++;
33638032Speter	}
33738032Speter
33842580Speter	s = p;
33942580Speter
34038032Speter	/* local-part */
34164565Sgshapiro	while (*p != '\0' && *p != '@' && *p != '>')
34264565Sgshapiro	{
34364565Sgshapiro		if (*p == '\\')
34464565Sgshapiro		{
34564565Sgshapiro			if (*++p == '\0')
34664565Sgshapiro				return NULL;
34764565Sgshapiro		}
34864565Sgshapiro		else if (*p == '\"')
34964565Sgshapiro		{
35064565Sgshapiro			p++;
35164565Sgshapiro			while (*p != '\0' && *p != '\"')
35264565Sgshapiro			{
35364565Sgshapiro				if (*p == '\\')
35464565Sgshapiro				{
35564565Sgshapiro					if (*++p == '\0')
35664565Sgshapiro						return NULL;
35764565Sgshapiro				}
35864565Sgshapiro				p++;
35938032Speter			}
36064565Sgshapiro			if (*p == '\0' || *(p + 1) == '\0')
36138032Speter				return NULL;
36238032Speter		}
36364565Sgshapiro		/* +detail ? */
36464565Sgshapiro		if (*p == '+' && rcpt)
36564565Sgshapiro			*p = '\0';
36664565Sgshapiro		p++;
36738032Speter	}
36838032Speter
36938032Speter	/* @domain */
37064565Sgshapiro	if (*p == '@')
37164565Sgshapiro	{
37264565Sgshapiro		if (rcpt)
37364565Sgshapiro			*p++ = '\0';
37464565Sgshapiro		while (*p != '\0' && *p != '>')
37538032Speter			p++;
37638032Speter	}
37738032Speter
37864565Sgshapiro	if (*p != '>')
37938032Speter		return NULL;
38064565Sgshapiro	else
38164565Sgshapiro		*p = '\0';
38264565Sgshapiro	p++;
38364565Sgshapiro
38464565Sgshapiro	if (*p != '\0' && *p != ' ')
38538032Speter		return NULL;
38664565Sgshapiro
38764565Sgshapiro	if (*s == '\0')
38842580Speter		s = MAILER_DAEMON;
38938032Speter
39064565Sgshapiro	l = strlen(s) + 1;
39190795Sgshapiro	if (l < 0)
39290795Sgshapiro		return NULL;
39364565Sgshapiro	p = malloc(l);
39464565Sgshapiro	if (p == NULL)
39564565Sgshapiro	{
39673191Sgshapiro		mailerr("421 4.3.0", "Memory exhausted");
39738032Speter		exit(EX_TEMPFAIL);
39838032Speter	}
39938032Speter
40090795Sgshapiro	(void) sm_strlcpy(p, s, l);
40138032Speter	return p;
40238032Speter}
40338032Speter
40438032Speterchar *
40538032Speterprocess_recipient(addr)
40638032Speter	char *addr;
40738032Speter{
40890795Sgshapiro	SM_MBDB_T user;
40990795Sgshapiro
41090795Sgshapiro	switch (sm_mbdb_lookup(addr, &user))
41190795Sgshapiro	{
41290795Sgshapiro	  case EX_OK:
41390795Sgshapiro		return NULL;
41490795Sgshapiro
41590795Sgshapiro	  case EX_NOUSER:
41673191Sgshapiro		return "550 5.1.1 User unknown";
41790795Sgshapiro
41890795Sgshapiro	  case EX_TEMPFAIL:
41990795Sgshapiro		return "451 4.3.0 User database failure; retry later";
42090795Sgshapiro
42190795Sgshapiro	  default:
42290795Sgshapiro		return "550 5.3.0 User database failure";
42390795Sgshapiro	}
42438032Speter}
42538032Speter
42638032Speter#define RCPT_GROW	30
42738032Speter
42838032Spetervoid
42973191Sgshapirodolmtp()
43038032Speter{
43138032Speter	char *return_path = NULL;
43238032Speter	char **rcpt_addr = NULL;
43338032Speter	int rcpt_num = 0;
43438032Speter	int rcpt_alloc = 0;
43590795Sgshapiro	bool gotlhlo = false;
43638032Speter	char *err;
43738032Speter	int msgfd;
43838032Speter	char *p;
43938032Speter	int i;
44064565Sgshapiro	char myhostname[1024];
44164565Sgshapiro	char buf[4096];
44238032Speter
44373191Sgshapiro	memset(myhostname, '\0', sizeof myhostname);
44464565Sgshapiro	(void) gethostname(myhostname, sizeof myhostname - 1);
44573191Sgshapiro	if (myhostname[0] == '\0')
44690795Sgshapiro		sm_strlcpy(myhostname, "localhost", sizeof myhostname);
44738032Speter
44838032Speter	printf("220 %s LMTP ready\r\n", myhostname);
44964565Sgshapiro	for (;;)
45064565Sgshapiro	{
45164565Sgshapiro		(void) fflush(stdout);
45264565Sgshapiro		if (fgets(buf, sizeof(buf) - 1, stdin) == NULL)
45338032Speter			exit(EX_OK);
45438032Speter		p = buf + strlen(buf) - 1;
45538032Speter		if (p >= buf && *p == '\n')
45638032Speter			*p-- = '\0';
45738032Speter		if (p >= buf && *p == '\r')
45838032Speter			*p-- = '\0';
45938032Speter
46064565Sgshapiro		switch (buf[0])
46164565Sgshapiro		{
46264565Sgshapiro		  case 'd':
46364565Sgshapiro		  case 'D':
46490795Sgshapiro			if (sm_strcasecmp(buf, "data") == 0)
46564565Sgshapiro			{
46690795Sgshapiro				bool inbody = false;
46773191Sgshapiro
46864565Sgshapiro				if (rcpt_num == 0)
46964565Sgshapiro				{
47073191Sgshapiro					mailerr("503 5.5.1", "No recipients");
47138032Speter					continue;
47238032Speter				}
47390795Sgshapiro				HoldErrs = true;
47490795Sgshapiro				msgfd = store(return_path, &inbody);
47590795Sgshapiro				HoldErrs = false;
47673191Sgshapiro				if (msgfd < 0 && !inbody)
47773191Sgshapiro				{
47873191Sgshapiro					flush_error();
47938032Speter					continue;
48073191Sgshapiro				}
48138032Speter
48264565Sgshapiro				for (i = 0; i < rcpt_num; i++)
48364565Sgshapiro				{
48473191Sgshapiro					if (msgfd < 0)
48573191Sgshapiro					{
48673191Sgshapiro						/* print error for rcpt */
48773191Sgshapiro						flush_error();
48873191Sgshapiro						continue;
48973191Sgshapiro					}
49038032Speter					p = strchr(rcpt_addr[i], '+');
49138032Speter					if (p != NULL)
49273191Sgshapiro						*p = '\0';
49373191Sgshapiro					deliver(msgfd, rcpt_addr[i]);
49438032Speter				}
49573191Sgshapiro				if (msgfd >= 0)
49673191Sgshapiro					(void) close(msgfd);
49738032Speter				goto rset;
49838032Speter			}
49938032Speter			goto syntaxerr;
50064565Sgshapiro			/* NOTREACHED */
50164565Sgshapiro			break;
50238032Speter
50364565Sgshapiro		  case 'l':
50464565Sgshapiro		  case 'L':
50590795Sgshapiro			if (sm_strncasecmp(buf, "lhlo ", 5) == 0)
50664565Sgshapiro			{
50764565Sgshapiro				/* check for duplicate per RFC 1651 4.2 */
50864565Sgshapiro				if (gotlhlo)
50964565Sgshapiro				{
51073191Sgshapiro					mailerr("503", "%s Duplicate LHLO",
51164565Sgshapiro					       myhostname);
51264565Sgshapiro					continue;
51364565Sgshapiro				}
51490795Sgshapiro				gotlhlo = true;
51564565Sgshapiro				printf("250-%s\r\n", myhostname);
51664565Sgshapiro				if (EightBitMime)
51764565Sgshapiro					printf("250-8BITMIME\r\n");
51864565Sgshapiro				printf("250-ENHANCEDSTATUSCODES\r\n");
51964565Sgshapiro				printf("250 PIPELINING\r\n");
52038032Speter				continue;
52138032Speter			}
52238032Speter			goto syntaxerr;
52364565Sgshapiro			/* NOTREACHED */
52464565Sgshapiro			break;
52538032Speter
52664565Sgshapiro		  case 'm':
52764565Sgshapiro		  case 'M':
52890795Sgshapiro			if (sm_strncasecmp(buf, "mail ", 5) == 0)
52964565Sgshapiro			{
53064565Sgshapiro				if (return_path != NULL)
53164565Sgshapiro				{
53273191Sgshapiro					mailerr("503 5.5.1",
53373191Sgshapiro						"Nested MAIL command");
53438032Speter					continue;
53538032Speter				}
53698125Sgshapiro				if (sm_strncasecmp(buf + 5, "from:", 5) != 0 ||
53764565Sgshapiro				    ((return_path = parseaddr(buf + 10,
53890795Sgshapiro							      false)) == NULL))
53964565Sgshapiro				{
54073191Sgshapiro					mailerr("501 5.5.4",
54173191Sgshapiro						"Syntax error in parameters");
54238032Speter					continue;
54338032Speter				}
54473191Sgshapiro				printf("250 2.5.0 Ok\r\n");
54538032Speter				continue;
54638032Speter			}
54738032Speter			goto syntaxerr;
54864565Sgshapiro			/* NOTREACHED */
54964565Sgshapiro			break;
55038032Speter
55164565Sgshapiro		  case 'n':
55264565Sgshapiro		  case 'N':
55390795Sgshapiro			if (sm_strcasecmp(buf, "noop") == 0)
55464565Sgshapiro			{
55573191Sgshapiro				printf("250 2.0.0 Ok\r\n");
55638032Speter				continue;
55738032Speter			}
55838032Speter			goto syntaxerr;
55964565Sgshapiro			/* NOTREACHED */
56064565Sgshapiro			break;
56138032Speter
56264565Sgshapiro		  case 'q':
56364565Sgshapiro		  case 'Q':
56490795Sgshapiro			if (sm_strcasecmp(buf, "quit") == 0)
56564565Sgshapiro			{
56673191Sgshapiro				printf("221 2.0.0 Bye\r\n");
56738032Speter				exit(EX_OK);
56838032Speter			}
56938032Speter			goto syntaxerr;
57064565Sgshapiro			/* NOTREACHED */
57164565Sgshapiro			break;
57238032Speter
57364565Sgshapiro		  case 'r':
57464565Sgshapiro		  case 'R':
57590795Sgshapiro			if (sm_strncasecmp(buf, "rcpt ", 5) == 0)
57664565Sgshapiro			{
57764565Sgshapiro				if (return_path == NULL)
57864565Sgshapiro				{
57973191Sgshapiro					mailerr("503 5.5.1",
58073191Sgshapiro						"Need MAIL command");
58138032Speter					continue;
58238032Speter				}
58364565Sgshapiro				if (rcpt_num >= rcpt_alloc)
58464565Sgshapiro				{
58538032Speter					rcpt_alloc += RCPT_GROW;
58638032Speter					rcpt_addr = (char **)
58766497Sgshapiro						REALLOC((char *) rcpt_addr,
58864565Sgshapiro							rcpt_alloc *
58964565Sgshapiro							sizeof(char **));
59064565Sgshapiro					if (rcpt_addr == NULL)
59164565Sgshapiro					{
59273191Sgshapiro						mailerr("421 4.3.0",
59373191Sgshapiro							"Memory exhausted");
59438032Speter						exit(EX_TEMPFAIL);
59538032Speter					}
59638032Speter				}
59790795Sgshapiro				if (sm_strncasecmp(buf + 5, "to:", 3) != 0 ||
59864565Sgshapiro				    ((rcpt_addr[rcpt_num] = parseaddr(buf + 8,
59990795Sgshapiro								      true)) == NULL))
60064565Sgshapiro				{
60173191Sgshapiro					mailerr("501 5.5.4",
60273191Sgshapiro						"Syntax error in parameters");
60338032Speter					continue;
60438032Speter				}
60573191Sgshapiro				err = process_recipient(rcpt_addr[rcpt_num]);
60673191Sgshapiro				if (err != NULL)
60764565Sgshapiro				{
60873191Sgshapiro					mailerr(NULL, "%s", err);
60938032Speter					continue;
61038032Speter				}
61138032Speter				rcpt_num++;
61273191Sgshapiro				printf("250 2.1.5 Ok\r\n");
61338032Speter				continue;
61438032Speter			}
61590795Sgshapiro			else if (sm_strcasecmp(buf, "rset") == 0)
61664565Sgshapiro			{
61773191Sgshapiro				printf("250 2.0.0 Ok\r\n");
61838032Speter
61964565Sgshapirorset:
62071348Sgshapiro				while (rcpt_num > 0)
62138032Speter					free(rcpt_addr[--rcpt_num]);
62238032Speter				if (return_path != NULL)
62338032Speter					free(return_path);
62438032Speter				return_path = NULL;
62538032Speter				continue;
62638032Speter			}
62738032Speter			goto syntaxerr;
62864565Sgshapiro			/* NOTREACHED */
62964565Sgshapiro			break;
63038032Speter
63164565Sgshapiro		  case 'v':
63264565Sgshapiro		  case 'V':
63390795Sgshapiro			if (sm_strncasecmp(buf, "vrfy ", 5) == 0)
63464565Sgshapiro			{
63573191Sgshapiro				printf("252 2.3.3 Try RCPT to attempt delivery\r\n");
63638032Speter				continue;
63738032Speter			}
63838032Speter			goto syntaxerr;
63964565Sgshapiro			/* NOTREACHED */
64064565Sgshapiro			break;
64138032Speter
64264565Sgshapiro		  default:
64338032Speter  syntaxerr:
64473191Sgshapiro			mailerr("500 5.5.2", "Syntax error");
64538032Speter			continue;
64664565Sgshapiro			/* NOTREACHED */
64764565Sgshapiro			break;
64838032Speter		}
64938032Speter	}
65038032Speter}
65138032Speter
65238032Speterint
65390795Sgshapirostore(from, inbody)
65438032Speter	char *from;
65573191Sgshapiro	bool *inbody;
65638032Speter{
65742580Speter	FILE *fp = NULL;
65838032Speter	time_t tval;
65990795Sgshapiro	bool eline;		/* previous line was empty */
66090795Sgshapiro	bool fullline = true;	/* current line is terminated */
66164565Sgshapiro	bool prevfl;		/* previous line was terminated */
66238032Speter	char line[2048];
66364565Sgshapiro	int fd;
66438032Speter	char tmpbuf[sizeof _PATH_LOCTMP + 1];
66538032Speter
66673191Sgshapiro	if (inbody != NULL)
66790795Sgshapiro		*inbody = false;
66873191Sgshapiro
66964565Sgshapiro	(void) umask(0077);
67090795Sgshapiro	(void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf);
67173191Sgshapiro	if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL)
67264565Sgshapiro	{
67373191Sgshapiro		mailerr("451 4.3.0", "Unable to open temporary file");
67473191Sgshapiro		return -1;
67538032Speter	}
67664565Sgshapiro	(void) unlink(tmpbuf);
67738032Speter
67864565Sgshapiro	if (LMTPMode)
67964565Sgshapiro	{
68073191Sgshapiro		printf("354 Go ahead\r\n");
68164565Sgshapiro		(void) fflush(stdout);
68238032Speter	}
68373191Sgshapiro	if (inbody != NULL)
68490795Sgshapiro		*inbody = true;
68538032Speter
68664565Sgshapiro	(void) time(&tval);
68764565Sgshapiro	(void) fprintf(fp, "From %s %s", from, ctime(&tval));
68838032Speter
68964565Sgshapiro#ifdef CONTENTLENGTH
69064565Sgshapiro	HeaderLength = 0;
69164565Sgshapiro	BodyLength = -1;
69264565Sgshapiro#endif /* CONTENTLENGTH */
69364565Sgshapiro
69438032Speter	line[0] = '\0';
69590795Sgshapiro	eline = true;
69673191Sgshapiro	while (fgets(line, sizeof(line), stdin) != (char *) NULL)
69764565Sgshapiro	{
69864565Sgshapiro		size_t line_len = 0;
69964565Sgshapiro		int peek;
70042580Speter
70164565Sgshapiro		prevfl = fullline;	/* preserve state of previous line */
70264565Sgshapiro		while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
70364565Sgshapiro			line_len++;
70464565Sgshapiro		line_len++;
70542580Speter
70664565Sgshapiro		/* Check for dot-stuffing */
70773191Sgshapiro		if (prevfl && LMTPMode && line[0] == '.')
70864565Sgshapiro		{
70964565Sgshapiro			if (line[1] == '\n' ||
71064565Sgshapiro			    (line[1] == '\r' && line[2] == '\n'))
71138032Speter				goto lmtpdot;
71264565Sgshapiro			memcpy(line, line + 1, line_len);
71364565Sgshapiro			line_len--;
71438032Speter		}
71564565Sgshapiro
71664565Sgshapiro		/* Check to see if we have the full line from fgets() */
71790795Sgshapiro		fullline = false;
71864565Sgshapiro		if (line_len > 0)
71964565Sgshapiro		{
72064565Sgshapiro			if (line[line_len - 1] == '\n')
72164565Sgshapiro			{
72264565Sgshapiro				if (line_len >= 2 &&
72364565Sgshapiro				    line[line_len - 2] == '\r')
72464565Sgshapiro				{
72564565Sgshapiro					line[line_len - 2] = '\n';
72664565Sgshapiro					line[line_len - 1] = '\0';
72764565Sgshapiro					line_len--;
72864565Sgshapiro				}
72990795Sgshapiro				fullline = true;
73064565Sgshapiro			}
73164565Sgshapiro			else if (line[line_len - 1] == '\r')
73264565Sgshapiro			{
73364565Sgshapiro				/* Did we just miss the CRLF? */
73464565Sgshapiro				peek = fgetc(stdin);
73564565Sgshapiro				if (peek == '\n')
73664565Sgshapiro				{
73764565Sgshapiro					line[line_len - 1] = '\n';
73890795Sgshapiro					fullline = true;
73964565Sgshapiro				}
74064565Sgshapiro				else
74164565Sgshapiro					(void) ungetc(peek, stdin);
74264565Sgshapiro			}
74364565Sgshapiro		}
74464565Sgshapiro		else
74590795Sgshapiro			fullline = true;
74664565Sgshapiro
74764565Sgshapiro#ifdef CONTENTLENGTH
74864565Sgshapiro		if (prevfl && line[0] == '\n' && HeaderLength == 0)
74964565Sgshapiro		{
75090795Sgshapiro			eline = false;
75173191Sgshapiro			if (fp != NULL)
75273191Sgshapiro				HeaderLength = ftell(fp);
75364565Sgshapiro			if (HeaderLength <= 0)
75464565Sgshapiro			{
75564565Sgshapiro				/*
75664565Sgshapiro				**  shouldn't happen, unless ftell() is
75764565Sgshapiro				**  badly broken
75864565Sgshapiro				*/
75964565Sgshapiro
76064565Sgshapiro				HeaderLength = -1;
76164565Sgshapiro			}
76264565Sgshapiro		}
76364565Sgshapiro#else /* CONTENTLENGTH */
76464565Sgshapiro		if (prevfl && line[0] == '\n')
76590795Sgshapiro			eline = true;
76664565Sgshapiro#endif /* CONTENTLENGTH */
76764565Sgshapiro		else
76864565Sgshapiro		{
76938032Speter			if (eline && line[0] == 'F' &&
77073191Sgshapiro			    fp != NULL &&
77138032Speter			    !memcmp(line, "From ", 5))
77266497Sgshapiro				(void) putc('>', fp);
77390795Sgshapiro			eline = false;
77464565Sgshapiro#ifdef CONTENTLENGTH
77564565Sgshapiro			/* discard existing "Content-Length:" headers */
77664565Sgshapiro			if (prevfl && HeaderLength == 0 &&
77764565Sgshapiro			    (line[0] == 'C' || line[0] == 'c') &&
77890795Sgshapiro			    sm_strncasecmp(line, ContentHdr, 15) == 0)
77964565Sgshapiro			{
78064565Sgshapiro				/*
78164565Sgshapiro				**  be paranoid: clear the line
78264565Sgshapiro				**  so no "wrong matches" may occur later
78364565Sgshapiro				*/
78464565Sgshapiro				line[0] = '\0';
78564565Sgshapiro				continue;
78664565Sgshapiro			}
78764565Sgshapiro#endif /* CONTENTLENGTH */
78864565Sgshapiro
78938032Speter		}
79073191Sgshapiro		if (fp != NULL)
79164565Sgshapiro		{
79273191Sgshapiro			(void) fwrite(line, sizeof(char), line_len, fp);
79373191Sgshapiro			if (ferror(fp))
79464565Sgshapiro			{
79542580Speter				mailerr("451 4.3.0",
79673191Sgshapiro					"Temporary file write error");
79764565Sgshapiro				(void) fclose(fp);
79873191Sgshapiro				fp = NULL;
79973191Sgshapiro				continue;
80038032Speter			}
80138032Speter		}
80238032Speter	}
80338032Speter
80473191Sgshapiro	/* check if an error occurred */
80573191Sgshapiro	if (fp == NULL)
80673191Sgshapiro		return -1;
80773191Sgshapiro
80873191Sgshapiro	if (LMTPMode)
80964565Sgshapiro	{
81038032Speter		/* Got a premature EOF -- toss message and exit */
81138032Speter		exit(EX_OK);
81238032Speter	}
81338032Speter
81438032Speter	/* If message not newline terminated, need an extra. */
81573191Sgshapiro	if (fp != NULL && strchr(line, '\n') == NULL)
81664565Sgshapiro		(void) putc('\n', fp);
81738032Speter
81838032Speter  lmtpdot:
81938032Speter
82064565Sgshapiro#ifdef CONTENTLENGTH
82173191Sgshapiro	if (fp != NULL)
82273191Sgshapiro		BodyLength = ftell(fp);
82364565Sgshapiro	if (HeaderLength == 0 && BodyLength > 0)	/* empty body */
82464565Sgshapiro	{
82564565Sgshapiro		HeaderLength = BodyLength;
82664565Sgshapiro		BodyLength = 0;
82764565Sgshapiro	}
82864565Sgshapiro	else
82964565Sgshapiro		BodyLength = BodyLength - HeaderLength - 1 ;
83064565Sgshapiro
83164565Sgshapiro	if (HeaderLength > 0 && BodyLength >= 0)
83264565Sgshapiro	{
83390795Sgshapiro		(void) sm_snprintf(line, sizeof line, "%lld\n",
83490795Sgshapiro				   (LONGLONG_T) BodyLength);
83590795Sgshapiro		(void) sm_strlcpy(&ContentHdr[16], line,
83690795Sgshapiro				  sizeof(ContentHdr) - 16);
83764565Sgshapiro	}
83864565Sgshapiro	else
83964565Sgshapiro		BodyLength = -1;	/* Something is wrong here */
84064565Sgshapiro#endif /* CONTENTLENGTH */
84164565Sgshapiro
84238032Speter	/* Output a newline; note, empty messages are allowed. */
84373191Sgshapiro	if (fp != NULL)
84473191Sgshapiro		(void) putc('\n', fp);
84538032Speter
84673191Sgshapiro	if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0)
84764565Sgshapiro	{
84873191Sgshapiro		mailerr("451 4.3.0", "Temporary file write error");
84973191Sgshapiro		if (fp != NULL)
85064565Sgshapiro			(void) fclose(fp);
85173191Sgshapiro		return -1;
85238032Speter	}
85364565Sgshapiro	return fd;
85438032Speter}
85538032Speter
85638032Spetervoid
85773191Sgshapirodeliver(fd, name)
85838032Speter	int fd;
85938032Speter	char *name;
86038032Speter{
86164565Sgshapiro	struct stat fsb;
86264565Sgshapiro	struct stat sb;
86364565Sgshapiro	char path[MAXPATHLEN];
86471348Sgshapiro	int mbfd = -1, nr = 0, nw, off;
86590795Sgshapiro	int exitval;
86638032Speter	char *p;
86773191Sgshapiro	char *errcode;
86838032Speter	off_t curoff;
86964565Sgshapiro#ifdef CONTENTLENGTH
87064565Sgshapiro	off_t headerbytes;
87164565Sgshapiro	int readamount;
87264565Sgshapiro#endif /* CONTENTLENGTH */
87398125Sgshapiro	char biffmsg[100], buf[8 * 1024];
87490795Sgshapiro	SM_MBDB_T user;
87538032Speter
87638032Speter	/*
87764565Sgshapiro	**  Disallow delivery to unknown names -- special mailboxes can be
87864565Sgshapiro	**  handled in the sendmail aliases file.
87964565Sgshapiro	*/
88073191Sgshapiro
88190795Sgshapiro	exitval = sm_mbdb_lookup(name, &user);
88290795Sgshapiro	switch (exitval)
88364565Sgshapiro	{
88490795Sgshapiro	  case EX_OK:
88590795Sgshapiro		break;
88690795Sgshapiro
88790795Sgshapiro	  case EX_NOUSER:
88890795Sgshapiro		exitval = EX_UNAVAILABLE;
88990795Sgshapiro		mailerr("550 5.1.1", "%s: User unknown", name);
89090795Sgshapiro		break;
89190795Sgshapiro
89290795Sgshapiro	  case EX_TEMPFAIL:
89390795Sgshapiro		mailerr("451 4.3.0", "%s: User database failure; retry later",
89490795Sgshapiro			name);
89590795Sgshapiro		break;
89690795Sgshapiro
89790795Sgshapiro	  default:
89890795Sgshapiro		exitval = EX_UNAVAILABLE;
89990795Sgshapiro		mailerr("550 5.3.0", "%s: User database failure", name);
90090795Sgshapiro		break;
90190795Sgshapiro	}
90290795Sgshapiro
90390795Sgshapiro	if (exitval != EX_OK)
90490795Sgshapiro	{
90590795Sgshapiro		if (ExitVal != EX_TEMPFAIL)
90690795Sgshapiro			ExitVal = exitval;
90738032Speter		return;
90838032Speter	}
90990795Sgshapiro
91038032Speter	endpwent();
91138032Speter
91238032Speter	/*
91364565Sgshapiro	**  Keep name reasonably short to avoid buffer overruns.
91464565Sgshapiro	**	This isn't necessary on BSD because of the proper
91564565Sgshapiro	**	definition of snprintf(), but it can cause problems
91664565Sgshapiro	**	on other systems.
91764565Sgshapiro	**  Also, clear out any bogus characters.
91864565Sgshapiro	*/
91938032Speter
92038032Speter	if (strlen(name) > 40)
92138032Speter		name[40] = '\0';
92238032Speter	for (p = name; *p != '\0'; p++)
92338032Speter	{
92438032Speter		if (!isascii(*p))
92538032Speter			*p &= 0x7f;
92638032Speter		else if (!isprint(*p))
92738032Speter			*p = '.';
92838032Speter	}
92938032Speter
93038032Speter
93190795Sgshapiro	if (HomeMailFile == NULL)
93290795Sgshapiro	{
93390795Sgshapiro		if (sm_snprintf(path, sizeof(path), "%s/%s",
93490795Sgshapiro				_PATH_MAILDIR, name) >= sizeof(path))
93590795Sgshapiro		{
93690795Sgshapiro			exitval = EX_UNAVAILABLE;
93790795Sgshapiro			mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
93890795Sgshapiro			return;
93990795Sgshapiro		}
94090795Sgshapiro	}
94190795Sgshapiro	else if (*user.mbdb_homedir == '\0')
94290795Sgshapiro	{
94390795Sgshapiro		exitval = EX_UNAVAILABLE;
94490795Sgshapiro		mailerr("550 5.1.1", "%s: User missing home directory", name);
94590795Sgshapiro		return;
94690795Sgshapiro	}
94790795Sgshapiro	else if (sm_snprintf(path, sizeof(path), "%s/%s",
94890795Sgshapiro			     user.mbdb_homedir, HomeMailFile) >= sizeof(path))
94990795Sgshapiro	{
95090795Sgshapiro		exitval = EX_UNAVAILABLE;
95190795Sgshapiro		mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
95290795Sgshapiro		return;
95390795Sgshapiro	}
95464565Sgshapiro
95564565Sgshapiro
95638032Speter	/*
95764565Sgshapiro	**  If the mailbox is linked or a symlink, fail.  There's an obvious
95864565Sgshapiro	**  race here, that the file was replaced with a symbolic link after
95964565Sgshapiro	**  the lstat returned, but before the open.  We attempt to detect
96064565Sgshapiro	**  this by comparing the original stat information and information
96164565Sgshapiro	**  returned by an fstat of the file descriptor returned by the open.
96264565Sgshapiro	**
96364565Sgshapiro	**  NB: this is a symptom of a larger problem, that the mail spooling
96464565Sgshapiro	**  directory is writeable by the wrong users.  If that directory is
96564565Sgshapiro	**  writeable, system security is compromised for other reasons, and
96664565Sgshapiro	**  it cannot be fixed here.
96764565Sgshapiro	**
96864565Sgshapiro	**  If we created the mailbox, set the owner/group.  If that fails,
96964565Sgshapiro	**  just return.  Another process may have already opened it, so we
97064565Sgshapiro	**  can't unlink it.  Historically, binmail set the owner/group at
97164565Sgshapiro	**  each mail delivery.  We no longer do this, assuming that if the
97264565Sgshapiro	**  ownership or permissions were changed there was a reason.
97364565Sgshapiro	**
97464565Sgshapiro	**  XXX
97564565Sgshapiro	**  open(2) should support flock'ing the file.
97664565Sgshapiro	*/
97764565Sgshapiro
97838032Spetertryagain:
97964565Sgshapiro#ifdef MAILLOCK
98064565Sgshapiro	p = name;
98164565Sgshapiro#else /* MAILLOCK */
98264565Sgshapiro	p = path;
98364565Sgshapiro#endif /* MAILLOCK */
98464565Sgshapiro	if ((off = lockmbox(p)) != 0)
98564565Sgshapiro	{
98664565Sgshapiro		if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL)
98764565Sgshapiro		{
98864565Sgshapiro			ExitVal = EX_TEMPFAIL;
98973191Sgshapiro			errcode = "451 4.3.0";
99064565Sgshapiro		}
99164565Sgshapiro		else
99273191Sgshapiro			errcode = "551 5.3.0";
99373191Sgshapiro
99473191Sgshapiro		mailerr(errcode, "lockmailbox %s failed; error code %d %s",
99590795Sgshapiro			p, off, errno > 0 ? sm_errstring(errno) : "");
99664565Sgshapiro		return;
99764565Sgshapiro	}
99864565Sgshapiro
99964565Sgshapiro	if (lstat(path, &sb) < 0)
100064565Sgshapiro	{
100164565Sgshapiro		int save_errno;
100264565Sgshapiro		int mode = S_IRUSR|S_IWUSR;
100390795Sgshapiro		gid_t gid = user.mbdb_gid;
100464565Sgshapiro
100564565Sgshapiro#ifdef MAILGID
100664565Sgshapiro		(void) umask(0007);
100764565Sgshapiro		gid = MAILGID;
100864565Sgshapiro		mode |= S_IRGRP|S_IWGRP;
100964565Sgshapiro#endif /* MAILGID */
101064565Sgshapiro
101166497Sgshapiro		mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|EXTRA_MODE,
101266497Sgshapiro			    mode);
101364565Sgshapiro		save_errno = errno;
101464565Sgshapiro
101538032Speter		if (lstat(path, &sb) < 0)
101638032Speter		{
101764565Sgshapiro			ExitVal = EX_CANTCREAT;
101842580Speter			mailerr("550 5.2.0",
101942580Speter				"%s: lstat: file changed after open", path);
102038032Speter			goto err1;
102138032Speter		}
102273191Sgshapiro		if (mbfd < 0)
102364565Sgshapiro		{
102464565Sgshapiro			if (save_errno == EEXIST)
102538032Speter				goto tryagain;
102666497Sgshapiro
102766497Sgshapiro			/* open failed, don't try again */
102866497Sgshapiro			mailerr("450 4.2.0", "%s: %s", path,
102990795Sgshapiro				sm_errstring(save_errno));
103066497Sgshapiro			goto err0;
103164565Sgshapiro		}
103290795Sgshapiro		else if (fchown(mbfd, user.mbdb_uid, gid) < 0)
103364565Sgshapiro		{
103438032Speter			mailerr("451 4.3.0", "chown %u.%u: %s",
103590795Sgshapiro				user.mbdb_uid, gid, name);
103638032Speter			goto err1;
103738032Speter		}
103866497Sgshapiro		else
103966497Sgshapiro		{
104066497Sgshapiro			/*
104166497Sgshapiro			**  open() was successful, now close it so can
104266497Sgshapiro			**  be opened as the right owner again.
104366497Sgshapiro			**  Paranoia: reset mbdf since the file descriptor
104466497Sgshapiro			**  is no longer valid; better safe than sorry.
104566497Sgshapiro			*/
104666497Sgshapiro
104790795Sgshapiro			sb.st_uid = user.mbdb_uid;
104866497Sgshapiro			(void) close(mbfd);
104966497Sgshapiro			mbfd = -1;
105066497Sgshapiro		}
105164565Sgshapiro	}
105264565Sgshapiro	else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode))
105364565Sgshapiro	{
105438032Speter		mailerr("550 5.2.0", "%s: irregular file", path);
105538032Speter		goto err0;
105664565Sgshapiro	}
105790795Sgshapiro	else if (sb.st_uid != user.mbdb_uid)
105864565Sgshapiro	{
105964565Sgshapiro		ExitVal = EX_CANTCREAT;
106038032Speter		mailerr("550 5.2.0", "%s: wrong ownership (%d)",
106190795Sgshapiro			path, (int) sb.st_uid);
106238032Speter		goto err0;
106364565Sgshapiro	}
106438032Speter
106566497Sgshapiro	/* change UID for quota checks */
106690795Sgshapiro	if (setreuid(0, user.mbdb_uid) < 0)
106764565Sgshapiro	{
106866497Sgshapiro		mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
106990795Sgshapiro			(int) user.mbdb_uid, sm_errstring(errno),
107090795Sgshapiro			(int) getuid(), (int) geteuid());
107166497Sgshapiro		goto err1;
107266497Sgshapiro	}
107366497Sgshapiro#ifdef DEBUG
107490795Sgshapiro	fprintf(stderr, "new euid = %d\n", (int) geteuid());
107566497Sgshapiro#endif /* DEBUG */
107666497Sgshapiro	mbfd = open(path, O_APPEND|O_WRONLY|EXTRA_MODE, 0);
107766497Sgshapiro	if (mbfd < 0)
107866497Sgshapiro	{
107990795Sgshapiro		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
108038032Speter		goto err0;
108164565Sgshapiro	}
108264565Sgshapiro	else if (fstat(mbfd, &fsb) < 0 ||
108364565Sgshapiro		 fsb.st_nlink != 1 ||
108464565Sgshapiro		 sb.st_nlink != 1 ||
108564565Sgshapiro		 !S_ISREG(fsb.st_mode) ||
108664565Sgshapiro		 sb.st_dev != fsb.st_dev ||
108764565Sgshapiro		 sb.st_ino != fsb.st_ino ||
108866497Sgshapiro# if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
108964565Sgshapiro		 sb.st_gen != fsb.st_gen ||
109066497Sgshapiro# endif /* HAS_ST_GEN && 0 */
109164565Sgshapiro		 sb.st_uid != fsb.st_uid)
109264565Sgshapiro	{
109364565Sgshapiro		ExitVal = EX_TEMPFAIL;
109442580Speter		mailerr("550 5.2.0", "%s: fstat: file changed after open",
109542580Speter			path);
109638032Speter		goto err1;
109738032Speter	}
109838032Speter
109990795Sgshapiro#if 0
110090795Sgshapiro	/*
110190795Sgshapiro	**  This code could be reused if we decide to add a
110290795Sgshapiro	**  per-user quota field to the sm_mbdb interface.
110390795Sgshapiro	*/
110464565Sgshapiro
110590795Sgshapiro	/*
110690795Sgshapiro	**  Fail if the user has a quota specified, and delivery of this
110790795Sgshapiro	**  message would exceed that quota.  We bounce such failures using
110890795Sgshapiro	**  EX_UNAVAILABLE, unless there were internal problems, since
110990795Sgshapiro	**  storing immense messages for later retries can cause queueing
111090795Sgshapiro	**  issues.
111190795Sgshapiro	*/
111290795Sgshapiro
111390795Sgshapiro	if (ui.quota > 0)
111490795Sgshapiro	{
111590795Sgshapiro		struct stat dsb;
111690795Sgshapiro
111790795Sgshapiro		if (fstat(fd, &dsb) < 0)
111890795Sgshapiro		{
111990795Sgshapiro			ExitVal = EX_TEMPFAIL;
112090795Sgshapiro			mailerr("451 4.3.0",
112190795Sgshapiro				"%s: fstat: can't stat temporary storage: %s",
112290795Sgshapiro				ui.mailspool, sm_errstring(errno));
112390795Sgshapiro			goto err1;
112490795Sgshapiro		}
112590795Sgshapiro
112690795Sgshapiro		if (dsb.st_size + sb.st_size + 1 > ui.quota)
112790795Sgshapiro		{
112890795Sgshapiro			ExitVal = EX_UNAVAILABLE;
112990795Sgshapiro			mailerr("551 5.2.2",
113090795Sgshapiro				"%s: Mailbox full or quota exceeded",
113190795Sgshapiro				ui.mailspool);
113290795Sgshapiro			goto err1;
113390795Sgshapiro		}
113490795Sgshapiro	}
113590795Sgshapiro#endif /* 0 */
113690795Sgshapiro
113738032Speter	/* Wait until we can get a lock on the file. */
113864565Sgshapiro	if (flock(mbfd, LOCK_EX) < 0)
113964565Sgshapiro	{
114090795Sgshapiro		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
114138032Speter		goto err1;
114238032Speter	}
114338032Speter
114464565Sgshapiro	if (!nobiff)
114564565Sgshapiro	{
114638084Speter		/* Get the starting offset of the new message for biff. */
114773191Sgshapiro		curoff = lseek(mbfd, (off_t) 0, SEEK_END);
114890795Sgshapiro		(void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n",
114990795Sgshapiro				   name, (LONGLONG_T) curoff);
115038084Speter	}
115138032Speter
115238032Speter	/* Copy the message into the file. */
115373191Sgshapiro	if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1)
115464565Sgshapiro	{
115573191Sgshapiro		mailerr("450 4.2.0", "Temporary file: %s",
115690795Sgshapiro			sm_errstring(errno));
115738032Speter		goto err1;
115838032Speter	}
115938032Speter#ifdef DEBUG
116090795Sgshapiro	fprintf(stderr, "before writing: euid = %d\n", (int) geteuid());
116164565Sgshapiro#endif /* DEBUG */
116264565Sgshapiro#ifdef CONTENTLENGTH
116364565Sgshapiro	headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ;
116464565Sgshapiro	for (;;)
116564565Sgshapiro	{
116664565Sgshapiro		if (headerbytes == 0)
116764565Sgshapiro		{
116890795Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr);
116964565Sgshapiro			nr = strlen(buf);
117064565Sgshapiro			headerbytes = -1;
117164565Sgshapiro			readamount = 0;
117264565Sgshapiro		}
117364565Sgshapiro		else if (headerbytes > sizeof(buf) || headerbytes < 0)
117464565Sgshapiro			readamount = sizeof(buf);
117564565Sgshapiro		else
117664565Sgshapiro			readamount = headerbytes;
117764565Sgshapiro		if (readamount != 0)
117864565Sgshapiro			nr = read(fd, buf, readamount);
117964565Sgshapiro		if (nr <= 0)
118064565Sgshapiro			break;
118164565Sgshapiro		if (headerbytes > 0)
118264565Sgshapiro			headerbytes -= nr ;
118364565Sgshapiro
118464565Sgshapiro#else /* CONTENTLENGTH */
118538032Speter	while ((nr = read(fd, buf, sizeof(buf))) > 0)
118664565Sgshapiro	{
118764565Sgshapiro#endif /* CONTENTLENGTH */
118838032Speter		for (off = 0; off < nr; off += nw)
118964565Sgshapiro		{
119064565Sgshapiro			if ((nw = write(mbfd, buf + off, nr - off)) < 0)
119164565Sgshapiro			{
119273191Sgshapiro				errcode = "450 4.2.0";
119364565Sgshapiro#ifdef EDQUOT
119473191Sgshapiro				if (errno == EDQUOT && BounceQuota)
119573191Sgshapiro					errcode = "552 5.2.2";
119664565Sgshapiro#endif /* EDQUOT */
119773191Sgshapiro				mailerr(errcode, "%s: %s",
119890795Sgshapiro					path, sm_errstring(errno));
119938032Speter				goto err3;
120038032Speter			}
120164565Sgshapiro		}
120264565Sgshapiro	}
120364565Sgshapiro	if (nr < 0)
120464565Sgshapiro	{
120573191Sgshapiro		mailerr("450 4.2.0", "Temporary file: %s",
120690795Sgshapiro			sm_errstring(errno));
120738032Speter		goto err3;
120838032Speter	}
120938032Speter
121038032Speter	/* Flush to disk, don't wait for update. */
121164565Sgshapiro	if (!nofsync && fsync(mbfd) < 0)
121264565Sgshapiro	{
121390795Sgshapiro		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
121438032Spetererr3:
121573191Sgshapiro		(void) setreuid(0, 0);
121638032Speter#ifdef DEBUG
121790795Sgshapiro		fprintf(stderr, "reset euid = %d\n", (int) geteuid());
121864565Sgshapiro#endif /* DEBUG */
121964565Sgshapiro		(void) ftruncate(mbfd, curoff);
122066497Sgshapiroerr1:		if (mbfd >= 0)
122166497Sgshapiro			(void) close(mbfd);
122238032Spetererr0:		unlockmbox();
122338032Speter		return;
122438032Speter	}
122538032Speter
122638032Speter	/* Close and check -- NFS doesn't write until the close. */
122764565Sgshapiro	if (close(mbfd))
122864565Sgshapiro	{
122973191Sgshapiro		errcode = "450 4.2.0";
123064565Sgshapiro#ifdef EDQUOT
123173191Sgshapiro		if (errno == EDQUOT && BounceQuota)
123273191Sgshapiro			errcode = "552 5.2.2";
123364565Sgshapiro#endif /* EDQUOT */
123490795Sgshapiro		mailerr(errcode, "%s: %s", path, sm_errstring(errno));
123564565Sgshapiro		(void) truncate(path, curoff);
123664565Sgshapiro	}
123764565Sgshapiro	else if (!nobiff)
123838032Speter		notifybiff(biffmsg);
123938032Speter
124064565Sgshapiro	if (setreuid(0, 0) < 0)
124164565Sgshapiro	{
124238032Speter		mailerr("450 4.2.0", "setreuid(0, 0): %s",
124390795Sgshapiro			sm_errstring(errno));
124438032Speter		goto err0;
124538032Speter	}
124638032Speter#ifdef DEBUG
124790795Sgshapiro	fprintf(stderr, "reset euid = %d\n", (int) geteuid());
124864565Sgshapiro#endif /* DEBUG */
124938032Speter	unlockmbox();
125064565Sgshapiro	if (LMTPMode)
125173191Sgshapiro		printf("250 2.1.5 %s Ok\r\n", name);
125238032Speter}
125338032Speter
125438032Speter/*
125564565Sgshapiro**  user.lock files are necessary for compatibility with other
125664565Sgshapiro**  systems, e.g., when the mail spool file is NFS exported.
125764565Sgshapiro**  Alas, mailbox locking is more than just a local matter.
125864565Sgshapiro**  EPA 11/94.
125964565Sgshapiro*/
126038032Speter
126190795Sgshapirobool	Locked = false;
126238032Speter
126364565Sgshapiro#ifdef MAILLOCK
126464565Sgshapiroint
126564565Sgshapirolockmbox(name)
126664565Sgshapiro	char *name;
126764565Sgshapiro{
126866497Sgshapiro	int r = 0;
126964565Sgshapiro
127064565Sgshapiro	if (Locked)
127164565Sgshapiro		return 0;
127264565Sgshapiro	if ((r = maillock(name, 15)) == L_SUCCESS)
127364565Sgshapiro	{
127490795Sgshapiro		Locked = true;
127564565Sgshapiro		return 0;
127664565Sgshapiro	}
127764565Sgshapiro	switch (r)
127864565Sgshapiro	{
127964565Sgshapiro	  case L_TMPLOCK:	/* Can't create tmp file */
128064565Sgshapiro	  case L_TMPWRITE:	/* Can't write pid into lockfile */
128164565Sgshapiro	  case L_MAXTRYS:	/* Failed after retrycnt attempts */
128264565Sgshapiro		errno = 0;
128364565Sgshapiro		r = EX_TEMPFAIL;
128464565Sgshapiro		break;
128564565Sgshapiro	  case L_ERROR:		/* Check errno for reason */
128664565Sgshapiro		r = errno;
128764565Sgshapiro		break;
128864565Sgshapiro	  default:		/* other permanent errors */
128964565Sgshapiro		errno = 0;
129064565Sgshapiro		r = EX_UNAVAILABLE;
129164565Sgshapiro		break;
129264565Sgshapiro	}
129364565Sgshapiro	return r;
129464565Sgshapiro}
129564565Sgshapiro
129638032Spetervoid
129764565Sgshapirounlockmbox()
129864565Sgshapiro{
129964565Sgshapiro	if (Locked)
130064565Sgshapiro		mailunlock();
130190795Sgshapiro	Locked = false;
130264565Sgshapiro}
130364565Sgshapiro#else /* MAILLOCK */
130464565Sgshapiro
130564565Sgshapirochar	LockName[MAXPATHLEN];
130664565Sgshapiro
130764565Sgshapiroint
130838032Speterlockmbox(path)
130938032Speter	char *path;
131038032Speter{
131138032Speter	int statfailed = 0;
131264565Sgshapiro	time_t start;
131338032Speter
131464565Sgshapiro	if (Locked)
131564565Sgshapiro		return 0;
131664565Sgshapiro	if (strlen(path) + 6 > sizeof LockName)
131764565Sgshapiro		return EX_SOFTWARE;
131890795Sgshapiro	(void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path);
131964565Sgshapiro	(void) time(&start);
132064565Sgshapiro	for (; ; sleep(5))
132164565Sgshapiro	{
132238032Speter		int fd;
132338032Speter		struct stat st;
132438032Speter		time_t now;
132538032Speter
132664565Sgshapiro		/* global timeout */
132764565Sgshapiro		(void) time(&now);
132864565Sgshapiro		if (now > start + LOCKTO_GLOB)
132964565Sgshapiro		{
133064565Sgshapiro			errno = 0;
133164565Sgshapiro			return EX_TEMPFAIL;
133264565Sgshapiro		}
133364565Sgshapiro		fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, 0);
133464565Sgshapiro		if (fd >= 0)
133564565Sgshapiro		{
133638032Speter			/* defeat lock checking programs which test pid */
133764565Sgshapiro			(void) write(fd, "0", 2);
133890795Sgshapiro			Locked = true;
133964565Sgshapiro			(void) close(fd);
134064565Sgshapiro			return 0;
134138032Speter		}
134264565Sgshapiro		if (stat(LockName, &st) < 0)
134364565Sgshapiro		{
134438032Speter			if (statfailed++ > 5)
134564565Sgshapiro			{
134664565Sgshapiro				errno = 0;
134764565Sgshapiro				return EX_TEMPFAIL;
134864565Sgshapiro			}
134938032Speter			continue;
135038032Speter		}
135138032Speter		statfailed = 0;
135264565Sgshapiro		(void) time(&now);
135364565Sgshapiro		if (now < st.st_ctime + LOCKTO_RM)
135438032Speter			continue;
135564565Sgshapiro
135664565Sgshapiro		/* try to remove stale lockfile */
135764565Sgshapiro		if (unlink(LockName) < 0)
135864565Sgshapiro			return errno;
135938032Speter	}
136038032Speter}
136138032Speter
136238032Spetervoid
136338032Speterunlockmbox()
136438032Speter{
136564565Sgshapiro	if (!Locked)
136638032Speter		return;
136764565Sgshapiro	(void) unlink(LockName);
136890795Sgshapiro	Locked = false;
136938032Speter}
137064565Sgshapiro#endif /* MAILLOCK */
137138032Speter
137238032Spetervoid
137338032Speternotifybiff(msg)
137438032Speter	char *msg;
137538032Speter{
137690795Sgshapiro	static bool initialized = false;
137738032Speter	static int f = -1;
137838032Speter	struct hostent *hp;
137938032Speter	struct servent *sp;
138038032Speter	int len;
138164565Sgshapiro	static struct sockaddr_in addr;
138238032Speter
138364565Sgshapiro	if (!initialized)
138464565Sgshapiro	{
138590795Sgshapiro		initialized = true;
138664565Sgshapiro
138738032Speter		/* Be silent if biff service not available. */
138864565Sgshapiro		if ((sp = getservbyname("biff", "udp")) == NULL ||
138964565Sgshapiro		    (hp = gethostbyname("localhost")) == NULL ||
139064565Sgshapiro		    hp->h_length != INADDRSZ)
139138032Speter			return;
139264565Sgshapiro
139338032Speter		addr.sin_family = hp->h_addrtype;
139464565Sgshapiro		memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ);
139538032Speter		addr.sin_port = sp->s_port;
139638032Speter	}
139764565Sgshapiro
139864565Sgshapiro	/* No message, just return */
139964565Sgshapiro	if (msg == NULL)
140038032Speter		return;
140164565Sgshapiro
140264565Sgshapiro	/* Couldn't initialize addr struct */
140364565Sgshapiro	if (addr.sin_family == AF_UNSPEC)
140464565Sgshapiro		return;
140564565Sgshapiro
140673191Sgshapiro	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
140764565Sgshapiro		return;
140838032Speter	len = strlen(msg) + 1;
140964565Sgshapiro	(void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr));
141038032Speter}
141138032Speter
141238032Spetervoid
141338032Speterusage()
141438032Speter{
141564565Sgshapiro	ExitVal = EX_USAGE;
141690795Sgshapiro	mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ...");
141764565Sgshapiro	exit(ExitVal);
141838032Speter}
141938032Speter
142038032Spetervoid
142190795Sgshapiro/*VARARGS2*/
142238032Speter#ifdef __STDC__
142338032Spetermailerr(const char *hdr, const char *fmt, ...)
142464565Sgshapiro#else /* __STDC__ */
142538032Spetermailerr(hdr, fmt, va_alist)
142638032Speter	const char *hdr;
142738032Speter	const char *fmt;
142838032Speter	va_dcl
142964565Sgshapiro#endif /* __STDC__ */
143038032Speter{
143173191Sgshapiro	size_t len = 0;
143290795Sgshapiro	SM_VA_LOCAL_DECL
143338032Speter
143473191Sgshapiro	(void) e_to_sys(errno);
143573191Sgshapiro
143690795Sgshapiro	SM_VA_START(ap, fmt);
143773191Sgshapiro
143890795Sgshapiro	if (LMTPMode && hdr != NULL)
143938032Speter	{
144090795Sgshapiro		sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr);
144190795Sgshapiro		len = strlen(ErrBuf);
144238032Speter	}
144390795Sgshapiro	(void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap);
144490795Sgshapiro	SM_VA_END(ap);
144573191Sgshapiro
144673191Sgshapiro	if (!HoldErrs)
144773191Sgshapiro		flush_error();
144873191Sgshapiro
144973191Sgshapiro	/* Log the message to syslog. */
145073191Sgshapiro	if (!LMTPMode)
145173191Sgshapiro		syslog(LOG_ERR, "%s", ErrBuf);
145238032Speter}
145338032Speter
145438032Spetervoid
145573191Sgshapiroflush_error()
145638032Speter{
145773191Sgshapiro	if (LMTPMode)
145873191Sgshapiro		printf("%s\r\n", ErrBuf);
145973191Sgshapiro	else
146038032Speter	{
146173191Sgshapiro		if (ExitVal != EX_USAGE)
146273191Sgshapiro			(void) fprintf(stderr, "mail.local: ");
146373191Sgshapiro		fprintf(stderr, "%s\n", ErrBuf);
146438032Speter	}
146538032Speter}
146638032Speter
146738032Speter/*
146838032Speter * e_to_sys --
146938032Speter *	Guess which errno's are temporary.  Gag me.
147038032Speter */
147173191Sgshapiro
147264565Sgshapiroint
147338032Spetere_to_sys(num)
147438032Speter	int num;
147538032Speter{
147638032Speter	/* Temporary failures override hard errors. */
147764565Sgshapiro	if (ExitVal == EX_TEMPFAIL)
147864565Sgshapiro		return ExitVal;
147938032Speter
148064565Sgshapiro	switch (num)		/* Hopefully temporary errors. */
148164565Sgshapiro	{
148264565Sgshapiro#ifdef EDQUOT
148364565Sgshapiro	  case EDQUOT:		/* Disc quota exceeded */
148473191Sgshapiro		if (BounceQuota)
148564565Sgshapiro		{
148664565Sgshapiro			ExitVal = EX_UNAVAILABLE;
148764565Sgshapiro			break;
148864565Sgshapiro		}
148964565Sgshapiro		/* FALLTHROUGH */
149064565Sgshapiro#endif /* EDQUOT */
149138032Speter#ifdef EAGAIN
149264565Sgshapiro	  case EAGAIN:		/* Resource temporarily unavailable */
149364565Sgshapiro#endif /* EAGAIN */
149438032Speter#ifdef EBUSY
149564565Sgshapiro	  case EBUSY:		/* Device busy */
149664565Sgshapiro#endif /* EBUSY */
149738032Speter#ifdef EPROCLIM
149864565Sgshapiro	  case EPROCLIM:	/* Too many processes */
149964565Sgshapiro#endif /* EPROCLIM */
150038032Speter#ifdef EUSERS
150164565Sgshapiro	  case EUSERS:		/* Too many users */
150264565Sgshapiro#endif /* EUSERS */
150338032Speter#ifdef ECONNABORTED
150464565Sgshapiro	  case ECONNABORTED:	/* Software caused connection abort */
150564565Sgshapiro#endif /* ECONNABORTED */
150638032Speter#ifdef ECONNREFUSED
150764565Sgshapiro	  case ECONNREFUSED:	/* Connection refused */
150864565Sgshapiro#endif /* ECONNREFUSED */
150938032Speter#ifdef ECONNRESET
151064565Sgshapiro	  case ECONNRESET:	/* Connection reset by peer */
151164565Sgshapiro#endif /* ECONNRESET */
151238032Speter#ifdef EDEADLK
151364565Sgshapiro	  case EDEADLK:		/* Resource deadlock avoided */
151464565Sgshapiro#endif /* EDEADLK */
151538032Speter#ifdef EFBIG
151664565Sgshapiro	  case EFBIG:		/* File too large */
151764565Sgshapiro#endif /* EFBIG */
151838032Speter#ifdef EHOSTDOWN
151964565Sgshapiro	  case EHOSTDOWN:	/* Host is down */
152064565Sgshapiro#endif /* EHOSTDOWN */
152138032Speter#ifdef EHOSTUNREACH
152264565Sgshapiro	  case EHOSTUNREACH:	/* No route to host */
152364565Sgshapiro#endif /* EHOSTUNREACH */
152438032Speter#ifdef EMFILE
152564565Sgshapiro	  case EMFILE:		/* Too many open files */
152664565Sgshapiro#endif /* EMFILE */
152738032Speter#ifdef ENETDOWN
152864565Sgshapiro	  case ENETDOWN:	/* Network is down */
152964565Sgshapiro#endif /* ENETDOWN */
153038032Speter#ifdef ENETRESET
153164565Sgshapiro	  case ENETRESET:	/* Network dropped connection on reset */
153264565Sgshapiro#endif /* ENETRESET */
153338032Speter#ifdef ENETUNREACH
153464565Sgshapiro	  case ENETUNREACH:	/* Network is unreachable */
153564565Sgshapiro#endif /* ENETUNREACH */
153638032Speter#ifdef ENFILE
153764565Sgshapiro	  case ENFILE:		/* Too many open files in system */
153864565Sgshapiro#endif /* ENFILE */
153938032Speter#ifdef ENOBUFS
154064565Sgshapiro	  case ENOBUFS:		/* No buffer space available */
154164565Sgshapiro#endif /* ENOBUFS */
154238032Speter#ifdef ENOMEM
154364565Sgshapiro	  case ENOMEM:		/* Cannot allocate memory */
154464565Sgshapiro#endif /* ENOMEM */
154538032Speter#ifdef ENOSPC
154664565Sgshapiro	  case ENOSPC:		/* No space left on device */
154764565Sgshapiro#endif /* ENOSPC */
154838032Speter#ifdef EROFS
154964565Sgshapiro	  case EROFS:		/* Read-only file system */
155064565Sgshapiro#endif /* EROFS */
155138032Speter#ifdef ESTALE
155264565Sgshapiro	  case ESTALE:		/* Stale NFS file handle */
155364565Sgshapiro#endif /* ESTALE */
155438032Speter#ifdef ETIMEDOUT
155564565Sgshapiro	  case ETIMEDOUT:	/* Connection timed out */
155664565Sgshapiro#endif /* ETIMEDOUT */
155738032Speter#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
155864565Sgshapiro	  case EWOULDBLOCK:	/* Operation would block. */
155964565Sgshapiro#endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */
156064565Sgshapiro		ExitVal = EX_TEMPFAIL;
156138032Speter		break;
156264565Sgshapiro
156364565Sgshapiro	  default:
156464565Sgshapiro		ExitVal = EX_UNAVAILABLE;
156538032Speter		break;
156638032Speter	}
156764565Sgshapiro	return ExitVal;
156838032Speter}
156938032Speter
157038032Speter#if defined(ultrix) || defined(_CRAY)
157138032Speter/*
157238032Speter * Copyright (c) 1987, 1993
157338032Speter *	The Regents of the University of California.  All rights reserved.
157438032Speter *
157538032Speter * Redistribution and use in source and binary forms, with or without
157638032Speter * modification, are permitted provided that the following conditions
157738032Speter * are met:
157838032Speter * 1. Redistributions of source code must retain the above copyright
157938032Speter *    notice, this list of conditions and the following disclaimer.
158038032Speter * 2. Redistributions in binary form must reproduce the above copyright
158138032Speter *    notice, this list of conditions and the following disclaimer in the
158238032Speter *    documentation and/or other materials provided with the distribution.
158338032Speter * 3. All advertising materials mentioning features or use of this software
158438032Speter *    must display the following acknowledgement:
158538032Speter *	This product includes software developed by the University of
158638032Speter *	California, Berkeley and its contributors.
158738032Speter * 4. Neither the name of the University nor the names of its contributors
158838032Speter *    may be used to endorse or promote products derived from this software
158938032Speter *    without specific prior written permission.
159038032Speter *
159138032Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
159238032Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
159338032Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
159438032Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
159538032Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
159638032Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
159738032Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
159838032Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
159938032Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
160038032Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
160138032Speter * SUCH DAMAGE.
160238032Speter */
160338032Speter
160464565Sgshapiro# if defined(LIBC_SCCS) && !defined(lint)
160538032Speterstatic char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
160664565Sgshapiro# endif /* defined(LIBC_SCCS) && !defined(lint) */
160738032Speter
160864565Sgshapiro# include <sys/types.h>
160964565Sgshapiro# include <sys/stat.h>
161064565Sgshapiro# include <fcntl.h>
161164565Sgshapiro# include <errno.h>
161264565Sgshapiro# include <stdio.h>
161364565Sgshapiro# include <ctype.h>
161438032Speter
161538032Speterstatic int _gettemp();
161638032Speter
161738032Spetermkstemp(path)
161838032Speter	char *path;
161938032Speter{
162038032Speter	int fd;
162138032Speter
162238032Speter	return (_gettemp(path, &fd) ? fd : -1);
162338032Speter}
162438032Speter
162538032Speterstatic
162638032Speter_gettemp(path, doopen)
162738032Speter	char *path;
162838032Speter	register int *doopen;
162938032Speter{
163038032Speter	extern int errno;
163138032Speter	register char *start, *trv;
163238032Speter	struct stat sbuf;
163390795Sgshapiro	unsigned int pid;
163438032Speter
163538032Speter	pid = getpid();
163638032Speter	for (trv = path; *trv; ++trv);		/* extra X's get set to 0's */
163764565Sgshapiro	while (*--trv == 'X')
163864565Sgshapiro	{
163938032Speter		*trv = (pid % 10) + '0';
164038032Speter		pid /= 10;
164138032Speter	}
164238032Speter
164338032Speter	/*
164438032Speter	 * check the target directory; if you have six X's and it
164538032Speter	 * doesn't exist this runs for a *very* long time.
164638032Speter	 */
164764565Sgshapiro	for (start = trv + 1;; --trv)
164864565Sgshapiro	{
164938032Speter		if (trv <= path)
165038032Speter			break;
165164565Sgshapiro		if (*trv == '/')
165264565Sgshapiro		{
165338032Speter			*trv = '\0';
165438032Speter			if (stat(path, &sbuf) < 0)
165538032Speter				return(0);
165664565Sgshapiro			if (!S_ISDIR(sbuf.st_mode))
165764565Sgshapiro			{
165838032Speter				errno = ENOTDIR;
165938032Speter				return(0);
166038032Speter			}
166138032Speter			*trv = '/';
166238032Speter			break;
166338032Speter		}
166438032Speter	}
166538032Speter
166664565Sgshapiro	for (;;)
166764565Sgshapiro	{
166864565Sgshapiro		if (doopen)
166964565Sgshapiro		{
167066497Sgshapiro			if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR,
167166497Sgshapiro					    0600)) >= 0)
167238032Speter				return(1);
167338032Speter			if (errno != EEXIST)
167438032Speter				return(0);
167538032Speter		}
167638032Speter		else if (stat(path, &sbuf) < 0)
167738032Speter			return(errno == ENOENT ? 1 : 0);
167838032Speter
167938032Speter		/* tricky little algorithm for backward compatibility */
168064565Sgshapiro		for (trv = start;;)
168164565Sgshapiro		{
168238032Speter			if (!*trv)
168338032Speter				return(0);
168438032Speter			if (*trv == 'z')
168538032Speter				*trv++ = 'a';
168664565Sgshapiro			else
168764565Sgshapiro			{
168838032Speter				if (isascii(*trv) && isdigit(*trv))
168938032Speter					*trv = 'a';
169038032Speter				else
169138032Speter					++*trv;
169238032Speter				break;
169338032Speter			}
169438032Speter		}
169538032Speter	}
169664565Sgshapiro	/* NOTREACHED */
169738032Speter}
169864565Sgshapiro#endif /* defined(ultrix) || defined(_CRAY) */
1699