142580Speter/*
2261194Sgshapiro * Copyright (c) 1998-2004 Proofpoint, 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 *
11102533Sgshapiro * $FreeBSD: stable/11/contrib/sendmail/mail.local/mail.local.c 363466 2020-07-24 00:23:26Z gshapiro $
12102533Sgshapiro *
1338032Speter */
1438032Speter
1590795Sgshapiro#include <sm/gen.h>
1690795Sgshapiro
1790795SgshapiroSM_IDSTR(copyright,
18261194Sgshapiro"@(#) Copyright (c) 1998-2004 Proofpoint, Inc. and its suppliers.\n\
1964565Sgshapiro	All rights reserved.\n\
2064565Sgshapiro     Copyright (c) 1990, 1993, 1994\n\
2190795Sgshapiro	The Regents of the University of California.  All rights reserved.\n")
2238032Speter
23266527SgshapiroSM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.257 2013-11-22 20:51:51 ca Exp $")
2438032Speter
2590795Sgshapiro#include <stdlib.h>
2690795Sgshapiro#include <sm/errstring.h>
2790795Sgshapiro#include <sm/io.h>
2890795Sgshapiro#include <sm/limits.h>
2990795Sgshapiro# include <unistd.h>
3090795Sgshapiro# ifdef EX_OK
3190795Sgshapiro#  undef EX_OK		/* unistd.h may have another use for this */
32363466Sgshapiro# endif
33120259Sgshapiro# define LOCKFILE_PMODE 0
3490795Sgshapiro#include <sm/mbdb.h>
3590795Sgshapiro#include <sm/sysexits.h>
3690795Sgshapiro
37132946Sgshapiro#ifndef HASHSPOOL
38132946Sgshapiro# define HASHSPOOL	0
39363466Sgshapiro#endif
40132946Sgshapiro#ifndef HASHSPOOLMD5
41132946Sgshapiro# define HASHSPOOLMD5	0
42363466Sgshapiro#endif
43132946Sgshapiro
4438032Speter/*
4564565Sgshapiro**  This is not intended to work on System V derived systems
4664565Sgshapiro**  such as Solaris or HP-UX, since they use a totally different
4790795Sgshapiro**  approach to mailboxes (essentially, they have a set-group-ID program
4890795Sgshapiro**  rather than set-user-ID, and they rely on the ability to "give away"
4964565Sgshapiro**  files to do their work).  IT IS NOT A BUG that this doesn't
5064565Sgshapiro**  work on such architectures.
5164565Sgshapiro*/
5238032Speter
5364565Sgshapiro
5490795Sgshapiro#include <stdio.h>
5590795Sgshapiro#include <errno.h>
5690795Sgshapiro#include <fcntl.h>
5790795Sgshapiro#include <sys/types.h>
5890795Sgshapiro#include <sys/stat.h>
5990795Sgshapiro#include <time.h>
6090795Sgshapiro#include <stdlib.h>
6166497Sgshapiro# include <sys/socket.h>
6266497Sgshapiro# include <sys/file.h>
6366497Sgshapiro# include <netinet/in.h>
6466497Sgshapiro# include <arpa/nameser.h>
6566497Sgshapiro# include <netdb.h>
6690795Sgshapiro# include <pwd.h>
6764565Sgshapiro
6890795Sgshapiro#include <sm/string.h>
6990795Sgshapiro#include <syslog.h>
7090795Sgshapiro#include <ctype.h>
7166497Sgshapiro
7290795Sgshapiro#include <sm/conf.h>
7390795Sgshapiro#include <sendmail/pathnames.h>
7464565Sgshapiro
75132946Sgshapiro#if HASHSPOOL
76132946Sgshapiro# define HASH_NONE	0
77132946Sgshapiro# define HASH_USER	1
78132946Sgshapiro# if HASHSPOOLMD5
79132946Sgshapiro#  define HASH_MD5	2
80132946Sgshapiro#  include <openssl/md5.h>
81363466Sgshapiro# endif
82132946Sgshapiro#endif /* HASHSPOOL */
8364565Sgshapiro
84182352Sgshapiro#if _FFR_SPOOL_PATH
85182352Sgshapiro	/*
86182352Sgshapiro	**  Override path to mail store at run time (using -p).
87182352Sgshapiro	**  From: Eugene Grosbein of Svyaz Service JSC
88182352Sgshapiro	**  See: http://www.freebsd.org/cgi/query-pr.cgi?pr=bin/114195
89182352Sgshapiro	**  NOTE: Update man page before adding this to a release.
90182352Sgshapiro	*/
91182352Sgshapiro#endif /* _FFR_SPOOL_PATH */
92132946Sgshapiro
93182352Sgshapiro
9490795Sgshapiro#ifndef LOCKTO_RM
9590795Sgshapiro# define LOCKTO_RM	300	/* timeout for stale lockfile removal */
96363466Sgshapiro#endif
9790795Sgshapiro#ifndef LOCKTO_GLOB
9890795Sgshapiro# define LOCKTO_GLOB	400	/* global timeout for lockfile creation */
99363466Sgshapiro#endif
10090795Sgshapiro
10164565Sgshapiro/* define a realloc() which works for NULL pointers */
10290795Sgshapiro#define REALLOC(ptr, size)	(((ptr) == NULL) ? malloc(size) : realloc(ptr, size))
10338032Speter
10438032Speter/*
10590795Sgshapiro**  If you don't have flock, you could try using lockf instead.
10690795Sgshapiro*/
10738032Speter
10890795Sgshapiro#ifdef LDA_USE_LOCKF
10990795Sgshapiro# define flock(a, b)	lockf(a, b, 0)
11090795Sgshapiro# ifdef LOCK_EX
11190795Sgshapiro#  undef LOCK_EX
112363466Sgshapiro# endif
11390795Sgshapiro# define LOCK_EX	F_LOCK
11490795Sgshapiro#endif /* LDA_USE_LOCKF */
11538032Speter
11690795Sgshapiro#ifndef LOCK_EX
11790795Sgshapiro# include <sys/file.h>
118363466Sgshapiro#endif
11938032Speter
12038032Speter/*
12164565Sgshapiro**  If you don't have setreuid, and you have saved uids, and you have
12264565Sgshapiro**  a seteuid() call that doesn't try to emulate using setuid(), then
12390795Sgshapiro**  you can try defining LDA_USE_SETEUID.
12464565Sgshapiro*/
12573191Sgshapiro
12690795Sgshapiro#ifdef LDA_USE_SETEUID
12790795Sgshapiro# define setreuid(r, e)		seteuid(e)
128363466Sgshapiro#endif
12938032Speter
13090795Sgshapiro#ifdef LDA_CONTENTLENGTH
13190795Sgshapiro# define CONTENTLENGTH	1
132363466Sgshapiro#endif
13373191Sgshapiro
13464565Sgshapiro#ifndef INADDRSZ
13564565Sgshapiro# define INADDRSZ	4		/* size of an IPv4 address in bytes */
136363466Sgshapiro#endif
13764565Sgshapiro
13890795Sgshapiro#ifdef MAILLOCK
13990795Sgshapiro# include <maillock.h>
140363466Sgshapiro#endif
14190795Sgshapiro
14242580Speter#ifndef MAILER_DAEMON
14342580Speter# define MAILER_DAEMON	"MAILER-DAEMON"
144363466Sgshapiro#endif
14542580Speter
14664565Sgshapiro#ifdef CONTENTLENGTH
14764565Sgshapirochar	ContentHdr[40] = "Content-Length: ";
14864565Sgshapirooff_t	HeaderLength;
14964565Sgshapirooff_t	BodyLength;
150363466Sgshapiro#endif
15138032Speter
15290795Sgshapirobool	EightBitMime = true;		/* advertise 8BITMIME in LMTP */
15373191Sgshapirochar	ErrBuf[10240];			/* error buffer */
15464565Sgshapiroint	ExitVal = EX_OK;		/* sysexits.h error value. */
15590795Sgshapirobool	nobiff = false;
15690795Sgshapirobool	nofsync = false;
15790795Sgshapirobool	HoldErrs = false;		/* Hold errors in ErrBuf */
15890795Sgshapirobool	LMTPMode = false;
15990795Sgshapirobool	BounceQuota = false;		/* permanent error when over quota */
160141862Sgshapirobool	CloseMBDB = false;
16190795Sgshapirochar	*HomeMailFile = NULL;		/* store mail in homedir */
16238032Speter
163132946Sgshapiro#if HASHSPOOL
164132946Sgshapiroint	HashType = HASH_NONE;
165132946Sgshapiroint	HashDepth = 0;
166132946Sgshapirobool	StripRcptDomain = true;
167363466Sgshapiro#else
168132946Sgshapiro# define StripRcptDomain true
169363466Sgshapiro#endif
170132946Sgshapirochar	SpoolPath[MAXPATHLEN];
171132946Sgshapiro
172141862Sgshapirochar	*parseaddr __P((char *, bool));
173141862Sgshapirochar	*process_recipient __P((char *));
174141862Sgshapirovoid	dolmtp __P((void));
17573191Sgshapirovoid	deliver __P((int, char *));
17664565Sgshapiroint	e_to_sys __P((int));
17764565Sgshapirovoid	notifybiff __P((char *));
17890795Sgshapiroint	store __P((char *, bool *));
17964565Sgshapirovoid	usage __P((void));
18064565Sgshapiroint	lockmbox __P((char *));
18164565Sgshapirovoid	unlockmbox __P((void));
18264565Sgshapirovoid	mailerr __P((const char *, const char *, ...));
18373191Sgshapirovoid	flush_error __P((void));
184132946Sgshapiro#if HASHSPOOL
185132946Sgshapiroconst char	*hashname __P((char *));
186363466Sgshapiro#endif
18764565Sgshapiro
18864565Sgshapiro
189168520Sgshapirostatic void sm_exit __P((int));
190168520Sgshapiro
191141862Sgshapirostatic void
192141862Sgshapirosm_exit(status)
193141862Sgshapiro	int status;
194141862Sgshapiro{
195141862Sgshapiro	if (CloseMBDB)
196141862Sgshapiro	{
197141862Sgshapiro		sm_mbdb_terminate();
198141862Sgshapiro		CloseMBDB = false;	/* not really necessary, but ... */
199141862Sgshapiro	}
200141862Sgshapiro	exit(status);
201141862Sgshapiro}
202141862Sgshapiro
20338032Speterint
20438032Spetermain(argc, argv)
20538032Speter	int argc;
20638032Speter	char *argv[];
20738032Speter{
20838032Speter	struct passwd *pw;
20964565Sgshapiro	int ch, fd;
21038032Speter	uid_t uid;
21138032Speter	char *from;
21290795Sgshapiro	char *mbdbname = "pw";
21390795Sgshapiro	int err;
21438032Speter	extern char *optarg;
21538032Speter	extern int optind;
21638032Speter
21764565Sgshapiro
21838032Speter	/* make sure we have some open file descriptors */
21938032Speter	for (fd = 10; fd < 30; fd++)
22038032Speter		(void) close(fd);
22138032Speter
22238032Speter	/* use a reasonable umask */
22338032Speter	(void) umask(0077);
22438032Speter
22564565Sgshapiro# ifdef LOG_MAIL
22638032Speter	openlog("mail.local", 0, LOG_MAIL);
227363466Sgshapiro# else
22838032Speter	openlog("mail.local", 0);
229363466Sgshapiro# endif
23038032Speter
23138032Speter	from = NULL;
232141862Sgshapiro
233141862Sgshapiro	/* XXX can this be converted to a compile time check? */
234132946Sgshapiro	if (sm_strlcpy(SpoolPath, _PATH_MAILDIR, sizeof(SpoolPath)) >=
235132946Sgshapiro	    sizeof(SpoolPath))
236132946Sgshapiro	{
237132946Sgshapiro		mailerr("421", "Configuration error: _PATH_MAILDIR too large");
238141862Sgshapiro		sm_exit(EX_CONFIG);
239132946Sgshapiro	}
240132946Sgshapiro#if HASHSPOOL
241132946Sgshapiro	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lH:p:ns")) != -1)
242132946Sgshapiro#else /* HASHSPOOL */
243182352Sgshapiro#  if _FFR_SPOOL_PATH
244182352Sgshapiro	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lp:s")) != -1)
245363466Sgshapiro#  else
24690795Sgshapiro	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:ls")) != -1)
247363466Sgshapiro#  endif
248132946Sgshapiro#endif /* HASHSPOOL */
24964565Sgshapiro	{
25064565Sgshapiro		switch(ch)
25164565Sgshapiro		{
25264565Sgshapiro		  case '7':		/* Do not advertise 8BITMIME */
25390795Sgshapiro			EightBitMime = false;
25438084Speter			break;
25564565Sgshapiro
25664565Sgshapiro		  case 'B':
25790825Sgshapiro			nobiff = true;
25838032Speter			break;
25964565Sgshapiro
26064565Sgshapiro		  case 'b':		/* bounce mail when over quota. */
26190795Sgshapiro			BounceQuota = true;
26264565Sgshapiro			break;
26364565Sgshapiro
26464565Sgshapiro		  case 'd':		/* Backward compatible. */
26564565Sgshapiro			break;
26664565Sgshapiro
26790795Sgshapiro		  case 'D':		/* mailbox database type */
26890795Sgshapiro			mbdbname = optarg;
26990795Sgshapiro			break;
27090795Sgshapiro
27164565Sgshapiro		  case 'f':
27264565Sgshapiro		  case 'r':		/* Backward compatible. */
27364565Sgshapiro			if (from != NULL)
27464565Sgshapiro			{
27573191Sgshapiro				mailerr(NULL, "Multiple -f options");
27638032Speter				usage();
27738032Speter			}
27838032Speter			from = optarg;
27938032Speter			break;
28064565Sgshapiro
28190795Sgshapiro		  case 'h':
28290795Sgshapiro			if (optarg != NULL || *optarg != '\0')
28390795Sgshapiro				HomeMailFile = optarg;
28490795Sgshapiro			else
28590795Sgshapiro			{
28690795Sgshapiro				mailerr(NULL, "-h: missing filename");
28790795Sgshapiro				usage();
28890795Sgshapiro			}
28990795Sgshapiro			break;
29090795Sgshapiro
29164565Sgshapiro		  case 'l':
29290795Sgshapiro			LMTPMode = true;
29338032Speter			break;
29464565Sgshapiro
29564565Sgshapiro		  case 's':
29638089Speter			nofsync++;
29738084Speter			break;
29864565Sgshapiro
299132946Sgshapiro#if HASHSPOOL
300132946Sgshapiro		  case 'H':
301132946Sgshapiro			if (optarg == NULL || *optarg == '\0')
302132946Sgshapiro			{
303132946Sgshapiro				mailerr(NULL, "-H: missing hashinfo");
304132946Sgshapiro				usage();
305132946Sgshapiro			}
306132946Sgshapiro			switch(optarg[0])
307132946Sgshapiro			{
308132946Sgshapiro			  case 'u':
309132946Sgshapiro				HashType = HASH_USER;
310132946Sgshapiro				break;
311132946Sgshapiro
312132946Sgshapiro# if HASHSPOOLMD5
313132946Sgshapiro			  case 'm':
314132946Sgshapiro				HashType = HASH_MD5;
315132946Sgshapiro				break;
316363466Sgshapiro# endif
317132946Sgshapiro
318132946Sgshapiro			  default:
319132946Sgshapiro				mailerr(NULL, "-H: unknown hash type");
320132946Sgshapiro				usage();
321132946Sgshapiro			}
322132946Sgshapiro			if (optarg[1] == '\0')
323132946Sgshapiro			{
324132946Sgshapiro				mailerr(NULL, "-H: invalid hash depth");
325132946Sgshapiro				usage();
326132946Sgshapiro			}
327132946Sgshapiro			HashDepth = atoi(&optarg[1]);
328132946Sgshapiro			if ((HashDepth <= 0) || ((HashDepth * 2) >= MAXPATHLEN))
329132946Sgshapiro			{
330132946Sgshapiro				mailerr(NULL, "-H: invalid hash depth");
331132946Sgshapiro				usage();
332132946Sgshapiro			}
333132946Sgshapiro			break;
334132946Sgshapiro
335182352Sgshapiro		  case 'n':
336182352Sgshapiro			StripRcptDomain = false;
337182352Sgshapiro			break;
338182352Sgshapiro#endif /* HASHSPOOL */
339182352Sgshapiro
340182352Sgshapiro#if HASHSPOOL || _FFR_SPOOL_PATH
341132946Sgshapiro		  case 'p':
342132946Sgshapiro			if (optarg == NULL || *optarg == '\0')
343132946Sgshapiro			{
344132946Sgshapiro				mailerr(NULL, "-p: missing spool path");
345132946Sgshapiro				usage();
346132946Sgshapiro			}
347132946Sgshapiro			if (sm_strlcpy(SpoolPath, optarg, sizeof(SpoolPath)) >=
348132946Sgshapiro			    sizeof(SpoolPath))
349132946Sgshapiro			{
350132946Sgshapiro				mailerr(NULL, "-p: invalid spool path");
351132946Sgshapiro				usage();
352132946Sgshapiro			}
353132946Sgshapiro			break;
354182352Sgshapiro#endif /* HASHSPOOL || _FFR_SPOOL_PATH */
355132946Sgshapiro
35664565Sgshapiro		  case '?':
35764565Sgshapiro		  default:
35838032Speter			usage();
35938032Speter		}
36064565Sgshapiro	}
36138032Speter	argc -= optind;
36238032Speter	argv += optind;
36338032Speter
36464565Sgshapiro	/* initialize biff structures */
36564565Sgshapiro	if (!nobiff)
36664565Sgshapiro		notifybiff(NULL);
36738032Speter
36890795Sgshapiro	err = sm_mbdb_initialize(mbdbname);
36990795Sgshapiro	if (err != EX_OK)
37090795Sgshapiro	{
37190795Sgshapiro		char *errcode = "521";
37290795Sgshapiro
37390795Sgshapiro		if (err == EX_TEMPFAIL)
37490795Sgshapiro			errcode = "421";
37590795Sgshapiro
37690795Sgshapiro		mailerr(errcode, "Can not open mailbox database %s: %s",
37790795Sgshapiro			mbdbname, sm_strexit(err));
378141862Sgshapiro		sm_exit(err);
37990795Sgshapiro	}
380141862Sgshapiro	CloseMBDB = true;
38190795Sgshapiro
38264565Sgshapiro	if (LMTPMode)
38373191Sgshapiro	{
38473191Sgshapiro		if (argc > 0)
38573191Sgshapiro		{
38673191Sgshapiro			mailerr("421", "Users should not be specified in command line if LMTP required");
387141862Sgshapiro			sm_exit(EX_TEMPFAIL);
38873191Sgshapiro		}
38973191Sgshapiro
39073191Sgshapiro		dolmtp();
39173191Sgshapiro		/* NOTREACHED */
392141862Sgshapiro		sm_exit(EX_OK);
39373191Sgshapiro	}
39473191Sgshapiro
39573191Sgshapiro	/* Non-LMTP from here on out */
396353589Sbrooks	if (*argv == NULL)
39738032Speter		usage();
39838032Speter
39938032Speter	/*
40064565Sgshapiro	**  If from not specified, use the name from getlogin() if the
40164565Sgshapiro	**  uid matches, otherwise, use the name from the password file
40264565Sgshapiro	**  corresponding to the uid.
40364565Sgshapiro	*/
40473191Sgshapiro
40538032Speter	uid = getuid();
40664565Sgshapiro	if (from == NULL && ((from = getlogin()) == NULL ||
40764565Sgshapiro			     (pw = getpwnam(from)) == NULL ||
40864565Sgshapiro			     pw->pw_uid != uid))
40964565Sgshapiro		from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???";
41064565Sgshapiro
41138032Speter	/*
41264565Sgshapiro	**  There is no way to distinguish the error status of one delivery
41364565Sgshapiro	**  from the rest of the deliveries.  So, if we failed hard on one
41464565Sgshapiro	**  or more deliveries, but had no failures on any of the others, we
41564565Sgshapiro	**  return a hard failure.  If we failed temporarily on one or more
41664565Sgshapiro	**  deliveries, we return a temporary failure regardless of the other
41764565Sgshapiro	**  failures.  This results in the delivery being reattempted later
41864565Sgshapiro	**  at the expense of repeated failures and multiple deliveries.
41964565Sgshapiro	*/
42073191Sgshapiro
42190795Sgshapiro	HoldErrs = true;
42290795Sgshapiro	fd = store(from, NULL);
42390795Sgshapiro	HoldErrs = false;
42473191Sgshapiro	if (fd < 0)
42573191Sgshapiro	{
42673191Sgshapiro		flush_error();
427141862Sgshapiro		sm_exit(ExitVal);
42873191Sgshapiro	}
42973191Sgshapiro	for (; *argv != NULL; ++argv)
43073191Sgshapiro		deliver(fd, *argv);
431141862Sgshapiro	sm_exit(ExitVal);
43264565Sgshapiro	/* NOTREACHED */
43364565Sgshapiro	return ExitVal;
43438032Speter}
43538032Speter
43638032Speterchar *
43764565Sgshapiroparseaddr(s, rcpt)
43838032Speter	char *s;
43964565Sgshapiro	bool rcpt;
44038032Speter{
44138032Speter	char *p;
44264565Sgshapiro	int l;
44338032Speter
44438032Speter	if (*s++ != '<')
44538032Speter		return NULL;
44638032Speter
44738032Speter	p = s;
44838032Speter
44938032Speter	/* at-domain-list */
45064565Sgshapiro	while (*p == '@')
45164565Sgshapiro	{
45238032Speter		p++;
45364565Sgshapiro		while (*p != ',' && *p != ':' && *p != '\0')
45438032Speter			p++;
45564565Sgshapiro		if (*p == '\0')
45638032Speter			return NULL;
45764565Sgshapiro
45864565Sgshapiro		/* Skip over , or : */
45964565Sgshapiro		p++;
46038032Speter	}
46138032Speter
46242580Speter	s = p;
46342580Speter
46438032Speter	/* local-part */
46564565Sgshapiro	while (*p != '\0' && *p != '@' && *p != '>')
46664565Sgshapiro	{
46764565Sgshapiro		if (*p == '\\')
46864565Sgshapiro		{
46964565Sgshapiro			if (*++p == '\0')
47064565Sgshapiro				return NULL;
47164565Sgshapiro		}
47264565Sgshapiro		else if (*p == '\"')
47364565Sgshapiro		{
47464565Sgshapiro			p++;
47564565Sgshapiro			while (*p != '\0' && *p != '\"')
47664565Sgshapiro			{
47764565Sgshapiro				if (*p == '\\')
47864565Sgshapiro				{
47964565Sgshapiro					if (*++p == '\0')
48064565Sgshapiro						return NULL;
48164565Sgshapiro				}
48264565Sgshapiro				p++;
48338032Speter			}
48464565Sgshapiro			if (*p == '\0' || *(p + 1) == '\0')
48538032Speter				return NULL;
48638032Speter		}
48764565Sgshapiro		/* +detail ? */
48864565Sgshapiro		if (*p == '+' && rcpt)
48964565Sgshapiro			*p = '\0';
49064565Sgshapiro		p++;
49138032Speter	}
49238032Speter
49338032Speter	/* @domain */
49464565Sgshapiro	if (*p == '@')
49564565Sgshapiro	{
49664565Sgshapiro		if (rcpt)
49764565Sgshapiro			*p++ = '\0';
49864565Sgshapiro		while (*p != '\0' && *p != '>')
49938032Speter			p++;
50038032Speter	}
50138032Speter
50264565Sgshapiro	if (*p != '>')
50338032Speter		return NULL;
50464565Sgshapiro	else
50564565Sgshapiro		*p = '\0';
50664565Sgshapiro	p++;
50764565Sgshapiro
50864565Sgshapiro	if (*p != '\0' && *p != ' ')
50938032Speter		return NULL;
51064565Sgshapiro
51164565Sgshapiro	if (*s == '\0')
51242580Speter		s = MAILER_DAEMON;
51338032Speter
51464565Sgshapiro	l = strlen(s) + 1;
51590795Sgshapiro	if (l < 0)
51690795Sgshapiro		return NULL;
51764565Sgshapiro	p = malloc(l);
51864565Sgshapiro	if (p == NULL)
51964565Sgshapiro	{
52073191Sgshapiro		mailerr("421 4.3.0", "Memory exhausted");
521141862Sgshapiro		sm_exit(EX_TEMPFAIL);
52238032Speter	}
52338032Speter
52490795Sgshapiro	(void) sm_strlcpy(p, s, l);
52538032Speter	return p;
52638032Speter}
52738032Speter
52838032Speterchar *
52938032Speterprocess_recipient(addr)
53038032Speter	char *addr;
53138032Speter{
53290795Sgshapiro	SM_MBDB_T user;
53390795Sgshapiro
53490795Sgshapiro	switch (sm_mbdb_lookup(addr, &user))
53590795Sgshapiro	{
53690795Sgshapiro	  case EX_OK:
53790795Sgshapiro		return NULL;
53890795Sgshapiro
53990795Sgshapiro	  case EX_NOUSER:
54073191Sgshapiro		return "550 5.1.1 User unknown";
54190795Sgshapiro
54290795Sgshapiro	  case EX_TEMPFAIL:
54390795Sgshapiro		return "451 4.3.0 User database failure; retry later";
54490795Sgshapiro
54590795Sgshapiro	  default:
54690795Sgshapiro		return "550 5.3.0 User database failure";
54790795Sgshapiro	}
54838032Speter}
54938032Speter
55038032Speter#define RCPT_GROW	30
55138032Speter
55238032Spetervoid
55373191Sgshapirodolmtp()
55438032Speter{
55538032Speter	char *return_path = NULL;
55638032Speter	char **rcpt_addr = NULL;
55738032Speter	int rcpt_num = 0;
55838032Speter	int rcpt_alloc = 0;
55990795Sgshapiro	bool gotlhlo = false;
56038032Speter	char *err;
56138032Speter	int msgfd;
56238032Speter	char *p;
56338032Speter	int i;
56464565Sgshapiro	char myhostname[1024];
56564565Sgshapiro	char buf[4096];
56638032Speter
56773191Sgshapiro	memset(myhostname, '\0', sizeof myhostname);
56864565Sgshapiro	(void) gethostname(myhostname, sizeof myhostname - 1);
56973191Sgshapiro	if (myhostname[0] == '\0')
57090795Sgshapiro		sm_strlcpy(myhostname, "localhost", sizeof myhostname);
57138032Speter
57238032Speter	printf("220 %s LMTP ready\r\n", myhostname);
57364565Sgshapiro	for (;;)
57464565Sgshapiro	{
57564565Sgshapiro		(void) fflush(stdout);
57664565Sgshapiro		if (fgets(buf, sizeof(buf) - 1, stdin) == NULL)
577141862Sgshapiro			sm_exit(EX_OK);
57838032Speter		p = buf + strlen(buf) - 1;
57938032Speter		if (p >= buf && *p == '\n')
58038032Speter			*p-- = '\0';
58138032Speter		if (p >= buf && *p == '\r')
58238032Speter			*p-- = '\0';
58338032Speter
58464565Sgshapiro		switch (buf[0])
58564565Sgshapiro		{
58664565Sgshapiro		  case 'd':
58764565Sgshapiro		  case 'D':
58890795Sgshapiro			if (sm_strcasecmp(buf, "data") == 0)
58964565Sgshapiro			{
59090795Sgshapiro				bool inbody = false;
59173191Sgshapiro
59264565Sgshapiro				if (rcpt_num == 0)
59364565Sgshapiro				{
59473191Sgshapiro					mailerr("503 5.5.1", "No recipients");
59538032Speter					continue;
59638032Speter				}
59790795Sgshapiro				HoldErrs = true;
59890795Sgshapiro				msgfd = store(return_path, &inbody);
59990795Sgshapiro				HoldErrs = false;
60073191Sgshapiro				if (msgfd < 0 && !inbody)
60173191Sgshapiro				{
60273191Sgshapiro					flush_error();
60338032Speter					continue;
60473191Sgshapiro				}
60538032Speter
60664565Sgshapiro				for (i = 0; i < rcpt_num; i++)
60764565Sgshapiro				{
60873191Sgshapiro					if (msgfd < 0)
60973191Sgshapiro					{
61073191Sgshapiro						/* print error for rcpt */
61173191Sgshapiro						flush_error();
61273191Sgshapiro						continue;
61373191Sgshapiro					}
61438032Speter					p = strchr(rcpt_addr[i], '+');
61538032Speter					if (p != NULL)
61673191Sgshapiro						*p = '\0';
61773191Sgshapiro					deliver(msgfd, rcpt_addr[i]);
61838032Speter				}
61973191Sgshapiro				if (msgfd >= 0)
62073191Sgshapiro					(void) close(msgfd);
62138032Speter				goto rset;
62238032Speter			}
62338032Speter			goto syntaxerr;
62464565Sgshapiro			/* NOTREACHED */
62564565Sgshapiro			break;
62638032Speter
62764565Sgshapiro		  case 'l':
62864565Sgshapiro		  case 'L':
62990795Sgshapiro			if (sm_strncasecmp(buf, "lhlo ", 5) == 0)
63064565Sgshapiro			{
63164565Sgshapiro				/* check for duplicate per RFC 1651 4.2 */
63264565Sgshapiro				if (gotlhlo)
63364565Sgshapiro				{
63473191Sgshapiro					mailerr("503", "%s Duplicate LHLO",
63564565Sgshapiro					       myhostname);
63664565Sgshapiro					continue;
63764565Sgshapiro				}
63890795Sgshapiro				gotlhlo = true;
63964565Sgshapiro				printf("250-%s\r\n", myhostname);
64064565Sgshapiro				if (EightBitMime)
64164565Sgshapiro					printf("250-8BITMIME\r\n");
64264565Sgshapiro				printf("250-ENHANCEDSTATUSCODES\r\n");
64364565Sgshapiro				printf("250 PIPELINING\r\n");
64438032Speter				continue;
64538032Speter			}
64638032Speter			goto syntaxerr;
64764565Sgshapiro			/* NOTREACHED */
64864565Sgshapiro			break;
64938032Speter
65064565Sgshapiro		  case 'm':
65164565Sgshapiro		  case 'M':
65290795Sgshapiro			if (sm_strncasecmp(buf, "mail ", 5) == 0)
65364565Sgshapiro			{
65464565Sgshapiro				if (return_path != NULL)
65564565Sgshapiro				{
65673191Sgshapiro					mailerr("503 5.5.1",
65773191Sgshapiro						"Nested MAIL command");
65838032Speter					continue;
65938032Speter				}
66098125Sgshapiro				if (sm_strncasecmp(buf + 5, "from:", 5) != 0 ||
66164565Sgshapiro				    ((return_path = parseaddr(buf + 10,
66290795Sgshapiro							      false)) == NULL))
66364565Sgshapiro				{
66473191Sgshapiro					mailerr("501 5.5.4",
66573191Sgshapiro						"Syntax error in parameters");
66638032Speter					continue;
66738032Speter				}
66873191Sgshapiro				printf("250 2.5.0 Ok\r\n");
66938032Speter				continue;
67038032Speter			}
67138032Speter			goto syntaxerr;
67264565Sgshapiro			/* NOTREACHED */
67364565Sgshapiro			break;
67438032Speter
67564565Sgshapiro		  case 'n':
67664565Sgshapiro		  case 'N':
67790795Sgshapiro			if (sm_strcasecmp(buf, "noop") == 0)
67864565Sgshapiro			{
67973191Sgshapiro				printf("250 2.0.0 Ok\r\n");
68038032Speter				continue;
68138032Speter			}
68238032Speter			goto syntaxerr;
68364565Sgshapiro			/* NOTREACHED */
68464565Sgshapiro			break;
68538032Speter
68664565Sgshapiro		  case 'q':
68764565Sgshapiro		  case 'Q':
68890795Sgshapiro			if (sm_strcasecmp(buf, "quit") == 0)
68964565Sgshapiro			{
69073191Sgshapiro				printf("221 2.0.0 Bye\r\n");
691141862Sgshapiro				sm_exit(EX_OK);
69238032Speter			}
69338032Speter			goto syntaxerr;
69464565Sgshapiro			/* NOTREACHED */
69564565Sgshapiro			break;
69638032Speter
69764565Sgshapiro		  case 'r':
69864565Sgshapiro		  case 'R':
69990795Sgshapiro			if (sm_strncasecmp(buf, "rcpt ", 5) == 0)
70064565Sgshapiro			{
70164565Sgshapiro				if (return_path == NULL)
70264565Sgshapiro				{
70373191Sgshapiro					mailerr("503 5.5.1",
70473191Sgshapiro						"Need MAIL command");
70538032Speter					continue;
70638032Speter				}
70764565Sgshapiro				if (rcpt_num >= rcpt_alloc)
70864565Sgshapiro				{
70938032Speter					rcpt_alloc += RCPT_GROW;
71038032Speter					rcpt_addr = (char **)
71166497Sgshapiro						REALLOC((char *) rcpt_addr,
71264565Sgshapiro							rcpt_alloc *
71364565Sgshapiro							sizeof(char **));
71464565Sgshapiro					if (rcpt_addr == NULL)
71564565Sgshapiro					{
71673191Sgshapiro						mailerr("421 4.3.0",
71773191Sgshapiro							"Memory exhausted");
718141862Sgshapiro						sm_exit(EX_TEMPFAIL);
71938032Speter					}
72038032Speter				}
72190795Sgshapiro				if (sm_strncasecmp(buf + 5, "to:", 3) != 0 ||
72264565Sgshapiro				    ((rcpt_addr[rcpt_num] = parseaddr(buf + 8,
723132946Sgshapiro								      StripRcptDomain)) == NULL))
72464565Sgshapiro				{
72573191Sgshapiro					mailerr("501 5.5.4",
72673191Sgshapiro						"Syntax error in parameters");
72738032Speter					continue;
72838032Speter				}
72973191Sgshapiro				err = process_recipient(rcpt_addr[rcpt_num]);
73073191Sgshapiro				if (err != NULL)
73164565Sgshapiro				{
73273191Sgshapiro					mailerr(NULL, "%s", err);
73338032Speter					continue;
73438032Speter				}
73538032Speter				rcpt_num++;
73673191Sgshapiro				printf("250 2.1.5 Ok\r\n");
73738032Speter				continue;
73838032Speter			}
73990795Sgshapiro			else if (sm_strcasecmp(buf, "rset") == 0)
74064565Sgshapiro			{
74173191Sgshapiro				printf("250 2.0.0 Ok\r\n");
74238032Speter
74364565Sgshapirorset:
74471348Sgshapiro				while (rcpt_num > 0)
74538032Speter					free(rcpt_addr[--rcpt_num]);
74638032Speter				if (return_path != NULL)
74738032Speter					free(return_path);
74838032Speter				return_path = NULL;
74938032Speter				continue;
75038032Speter			}
75138032Speter			goto syntaxerr;
75264565Sgshapiro			/* NOTREACHED */
75364565Sgshapiro			break;
75438032Speter
75564565Sgshapiro		  case 'v':
75664565Sgshapiro		  case 'V':
75790795Sgshapiro			if (sm_strncasecmp(buf, "vrfy ", 5) == 0)
75864565Sgshapiro			{
75973191Sgshapiro				printf("252 2.3.3 Try RCPT to attempt delivery\r\n");
76038032Speter				continue;
76138032Speter			}
76238032Speter			goto syntaxerr;
76364565Sgshapiro			/* NOTREACHED */
76464565Sgshapiro			break;
76538032Speter
76664565Sgshapiro		  default:
76738032Speter  syntaxerr:
76873191Sgshapiro			mailerr("500 5.5.2", "Syntax error");
76938032Speter			continue;
77064565Sgshapiro			/* NOTREACHED */
77164565Sgshapiro			break;
77238032Speter		}
77338032Speter	}
77438032Speter}
77538032Speter
77638032Speterint
77790795Sgshapirostore(from, inbody)
77838032Speter	char *from;
77973191Sgshapiro	bool *inbody;
78038032Speter{
78142580Speter	FILE *fp = NULL;
78238032Speter	time_t tval;
78390795Sgshapiro	bool eline;		/* previous line was empty */
78490795Sgshapiro	bool fullline = true;	/* current line is terminated */
78564565Sgshapiro	bool prevfl;		/* previous line was terminated */
78638032Speter	char line[2048];
78764565Sgshapiro	int fd;
78838032Speter	char tmpbuf[sizeof _PATH_LOCTMP + 1];
78938032Speter
79073191Sgshapiro	if (inbody != NULL)
79190795Sgshapiro		*inbody = false;
79273191Sgshapiro
79364565Sgshapiro	(void) umask(0077);
79490795Sgshapiro	(void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf);
79573191Sgshapiro	if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL)
79664565Sgshapiro	{
797110563Sgshapiro		if (fd >= 0)
798110563Sgshapiro			(void) close(fd);
79973191Sgshapiro		mailerr("451 4.3.0", "Unable to open temporary file");
80073191Sgshapiro		return -1;
80138032Speter	}
80264565Sgshapiro	(void) unlink(tmpbuf);
80338032Speter
80464565Sgshapiro	if (LMTPMode)
80564565Sgshapiro	{
80673191Sgshapiro		printf("354 Go ahead\r\n");
80764565Sgshapiro		(void) fflush(stdout);
80838032Speter	}
80973191Sgshapiro	if (inbody != NULL)
81090795Sgshapiro		*inbody = true;
81138032Speter
81264565Sgshapiro	(void) time(&tval);
81364565Sgshapiro	(void) fprintf(fp, "From %s %s", from, ctime(&tval));
81438032Speter
81564565Sgshapiro#ifdef CONTENTLENGTH
81664565Sgshapiro	HeaderLength = 0;
81764565Sgshapiro	BodyLength = -1;
818363466Sgshapiro#endif
81964565Sgshapiro
82038032Speter	line[0] = '\0';
82190795Sgshapiro	eline = true;
82273191Sgshapiro	while (fgets(line, sizeof(line), stdin) != (char *) NULL)
82364565Sgshapiro	{
82464565Sgshapiro		size_t line_len = 0;
82564565Sgshapiro		int peek;
82642580Speter
82764565Sgshapiro		prevfl = fullline;	/* preserve state of previous line */
82864565Sgshapiro		while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
82964565Sgshapiro			line_len++;
83064565Sgshapiro		line_len++;
83142580Speter
83264565Sgshapiro		/* Check for dot-stuffing */
83373191Sgshapiro		if (prevfl && LMTPMode && line[0] == '.')
83464565Sgshapiro		{
83564565Sgshapiro			if (line[1] == '\n' ||
83664565Sgshapiro			    (line[1] == '\r' && line[2] == '\n'))
83738032Speter				goto lmtpdot;
83864565Sgshapiro			memcpy(line, line + 1, line_len);
83964565Sgshapiro			line_len--;
84038032Speter		}
84164565Sgshapiro
84264565Sgshapiro		/* Check to see if we have the full line from fgets() */
84390795Sgshapiro		fullline = false;
84464565Sgshapiro		if (line_len > 0)
84564565Sgshapiro		{
84664565Sgshapiro			if (line[line_len - 1] == '\n')
84764565Sgshapiro			{
84864565Sgshapiro				if (line_len >= 2 &&
84964565Sgshapiro				    line[line_len - 2] == '\r')
85064565Sgshapiro				{
85164565Sgshapiro					line[line_len - 2] = '\n';
85264565Sgshapiro					line[line_len - 1] = '\0';
85364565Sgshapiro					line_len--;
85464565Sgshapiro				}
85590795Sgshapiro				fullline = true;
85664565Sgshapiro			}
85764565Sgshapiro			else if (line[line_len - 1] == '\r')
85864565Sgshapiro			{
85964565Sgshapiro				/* Did we just miss the CRLF? */
86064565Sgshapiro				peek = fgetc(stdin);
86164565Sgshapiro				if (peek == '\n')
86264565Sgshapiro				{
86364565Sgshapiro					line[line_len - 1] = '\n';
86490795Sgshapiro					fullline = true;
86564565Sgshapiro				}
86664565Sgshapiro				else
86764565Sgshapiro					(void) ungetc(peek, stdin);
86864565Sgshapiro			}
86964565Sgshapiro		}
87064565Sgshapiro		else
87190795Sgshapiro			fullline = true;
87264565Sgshapiro
87364565Sgshapiro#ifdef CONTENTLENGTH
87464565Sgshapiro		if (prevfl && line[0] == '\n' && HeaderLength == 0)
87564565Sgshapiro		{
87690795Sgshapiro			eline = false;
87773191Sgshapiro			if (fp != NULL)
87873191Sgshapiro				HeaderLength = ftell(fp);
87964565Sgshapiro			if (HeaderLength <= 0)
88064565Sgshapiro			{
88164565Sgshapiro				/*
88264565Sgshapiro				**  shouldn't happen, unless ftell() is
88364565Sgshapiro				**  badly broken
88464565Sgshapiro				*/
88564565Sgshapiro
88664565Sgshapiro				HeaderLength = -1;
88764565Sgshapiro			}
88864565Sgshapiro		}
88964565Sgshapiro#else /* CONTENTLENGTH */
89064565Sgshapiro		if (prevfl && line[0] == '\n')
89190795Sgshapiro			eline = true;
89264565Sgshapiro#endif /* CONTENTLENGTH */
89364565Sgshapiro		else
89464565Sgshapiro		{
89538032Speter			if (eline && line[0] == 'F' &&
89673191Sgshapiro			    fp != NULL &&
89738032Speter			    !memcmp(line, "From ", 5))
89866497Sgshapiro				(void) putc('>', fp);
89990795Sgshapiro			eline = false;
90064565Sgshapiro#ifdef CONTENTLENGTH
90164565Sgshapiro			/* discard existing "Content-Length:" headers */
90264565Sgshapiro			if (prevfl && HeaderLength == 0 &&
90364565Sgshapiro			    (line[0] == 'C' || line[0] == 'c') &&
90490795Sgshapiro			    sm_strncasecmp(line, ContentHdr, 15) == 0)
90564565Sgshapiro			{
90664565Sgshapiro				/*
90764565Sgshapiro				**  be paranoid: clear the line
90864565Sgshapiro				**  so no "wrong matches" may occur later
90964565Sgshapiro				*/
91064565Sgshapiro				line[0] = '\0';
91164565Sgshapiro				continue;
91264565Sgshapiro			}
91364565Sgshapiro#endif /* CONTENTLENGTH */
91464565Sgshapiro
91538032Speter		}
91673191Sgshapiro		if (fp != NULL)
91764565Sgshapiro		{
91873191Sgshapiro			(void) fwrite(line, sizeof(char), line_len, fp);
91973191Sgshapiro			if (ferror(fp))
92064565Sgshapiro			{
92142580Speter				mailerr("451 4.3.0",
92273191Sgshapiro					"Temporary file write error");
92364565Sgshapiro				(void) fclose(fp);
92473191Sgshapiro				fp = NULL;
92573191Sgshapiro				continue;
92638032Speter			}
92738032Speter		}
92838032Speter	}
92938032Speter
93073191Sgshapiro	/* check if an error occurred */
93173191Sgshapiro	if (fp == NULL)
93273191Sgshapiro		return -1;
93373191Sgshapiro
93473191Sgshapiro	if (LMTPMode)
93564565Sgshapiro	{
93638032Speter		/* Got a premature EOF -- toss message and exit */
937141862Sgshapiro		sm_exit(EX_OK);
93838032Speter	}
93938032Speter
94038032Speter	/* If message not newline terminated, need an extra. */
94173191Sgshapiro	if (fp != NULL && strchr(line, '\n') == NULL)
94264565Sgshapiro		(void) putc('\n', fp);
94338032Speter
94438032Speter  lmtpdot:
94538032Speter
94664565Sgshapiro#ifdef CONTENTLENGTH
94773191Sgshapiro	if (fp != NULL)
94873191Sgshapiro		BodyLength = ftell(fp);
94964565Sgshapiro	if (HeaderLength == 0 && BodyLength > 0)	/* empty body */
95064565Sgshapiro	{
95164565Sgshapiro		HeaderLength = BodyLength;
95264565Sgshapiro		BodyLength = 0;
95364565Sgshapiro	}
95464565Sgshapiro	else
95564565Sgshapiro		BodyLength = BodyLength - HeaderLength - 1 ;
95664565Sgshapiro
95764565Sgshapiro	if (HeaderLength > 0 && BodyLength >= 0)
95864565Sgshapiro	{
95990795Sgshapiro		(void) sm_snprintf(line, sizeof line, "%lld\n",
96090795Sgshapiro				   (LONGLONG_T) BodyLength);
96190795Sgshapiro		(void) sm_strlcpy(&ContentHdr[16], line,
96290795Sgshapiro				  sizeof(ContentHdr) - 16);
96364565Sgshapiro	}
96464565Sgshapiro	else
96564565Sgshapiro		BodyLength = -1;	/* Something is wrong here */
96664565Sgshapiro#endif /* CONTENTLENGTH */
96764565Sgshapiro
96838032Speter	/* Output a newline; note, empty messages are allowed. */
96973191Sgshapiro	if (fp != NULL)
97073191Sgshapiro		(void) putc('\n', fp);
97138032Speter
97273191Sgshapiro	if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0)
97364565Sgshapiro	{
97473191Sgshapiro		mailerr("451 4.3.0", "Temporary file write error");
97573191Sgshapiro		if (fp != NULL)
97664565Sgshapiro			(void) fclose(fp);
97773191Sgshapiro		return -1;
97838032Speter	}
97964565Sgshapiro	return fd;
98038032Speter}
98138032Speter
98238032Spetervoid
98373191Sgshapirodeliver(fd, name)
98438032Speter	int fd;
98538032Speter	char *name;
98638032Speter{
98764565Sgshapiro	struct stat fsb;
98864565Sgshapiro	struct stat sb;
98964565Sgshapiro	char path[MAXPATHLEN];
99071348Sgshapiro	int mbfd = -1, nr = 0, nw, off;
99190795Sgshapiro	int exitval;
99238032Speter	char *p;
99373191Sgshapiro	char *errcode;
994120259Sgshapiro	off_t curoff, cursize;
99564565Sgshapiro#ifdef CONTENTLENGTH
99664565Sgshapiro	off_t headerbytes;
99764565Sgshapiro	int readamount;
998363466Sgshapiro#endif
99998125Sgshapiro	char biffmsg[100], buf[8 * 1024];
100090795Sgshapiro	SM_MBDB_T user;
100138032Speter
100238032Speter	/*
100364565Sgshapiro	**  Disallow delivery to unknown names -- special mailboxes can be
100464565Sgshapiro	**  handled in the sendmail aliases file.
100564565Sgshapiro	*/
100673191Sgshapiro
100790795Sgshapiro	exitval = sm_mbdb_lookup(name, &user);
100890795Sgshapiro	switch (exitval)
100964565Sgshapiro	{
101090795Sgshapiro	  case EX_OK:
101190795Sgshapiro		break;
101290795Sgshapiro
101390795Sgshapiro	  case EX_NOUSER:
101490795Sgshapiro		exitval = EX_UNAVAILABLE;
101590795Sgshapiro		mailerr("550 5.1.1", "%s: User unknown", name);
101690795Sgshapiro		break;
101790795Sgshapiro
101890795Sgshapiro	  case EX_TEMPFAIL:
101990795Sgshapiro		mailerr("451 4.3.0", "%s: User database failure; retry later",
102090795Sgshapiro			name);
102190795Sgshapiro		break;
102290795Sgshapiro
102390795Sgshapiro	  default:
102490795Sgshapiro		exitval = EX_UNAVAILABLE;
102590795Sgshapiro		mailerr("550 5.3.0", "%s: User database failure", name);
102690795Sgshapiro		break;
102790795Sgshapiro	}
102890795Sgshapiro
102990795Sgshapiro	if (exitval != EX_OK)
103090795Sgshapiro	{
103190795Sgshapiro		if (ExitVal != EX_TEMPFAIL)
103290795Sgshapiro			ExitVal = exitval;
103338032Speter		return;
103438032Speter	}
103590795Sgshapiro
103638032Speter	endpwent();
103738032Speter
103838032Speter	/*
103964565Sgshapiro	**  Keep name reasonably short to avoid buffer overruns.
104064565Sgshapiro	**	This isn't necessary on BSD because of the proper
104164565Sgshapiro	**	definition of snprintf(), but it can cause problems
104264565Sgshapiro	**	on other systems.
104364565Sgshapiro	**  Also, clear out any bogus characters.
104464565Sgshapiro	*/
104538032Speter
1046132946Sgshapiro#if !HASHSPOOL
104738032Speter	if (strlen(name) > 40)
104838032Speter		name[40] = '\0';
104938032Speter	for (p = name; *p != '\0'; p++)
105038032Speter	{
105138032Speter		if (!isascii(*p))
105238032Speter			*p &= 0x7f;
105338032Speter		else if (!isprint(*p))
105438032Speter			*p = '.';
105538032Speter	}
1056132946Sgshapiro#endif /* !HASHSPOOL */
105738032Speter
105838032Speter
105990795Sgshapiro	if (HomeMailFile == NULL)
106090795Sgshapiro	{
1061132946Sgshapiro		if (sm_strlcpyn(path, sizeof(path),
1062132946Sgshapiro#if HASHSPOOL
1063132946Sgshapiro				4,
1064363466Sgshapiro#else
1065132946Sgshapiro				3,
1066363466Sgshapiro#endif
1067132946Sgshapiro				SpoolPath, "/",
1068132946Sgshapiro#if HASHSPOOL
1069132946Sgshapiro				hashname(name),
1070363466Sgshapiro#endif
1071132946Sgshapiro				name) >= sizeof(path))
107290795Sgshapiro		{
107390795Sgshapiro			exitval = EX_UNAVAILABLE;
107490795Sgshapiro			mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
107590795Sgshapiro			return;
107690795Sgshapiro		}
107790795Sgshapiro	}
107890795Sgshapiro	else if (*user.mbdb_homedir == '\0')
107990795Sgshapiro	{
108090795Sgshapiro		exitval = EX_UNAVAILABLE;
108190795Sgshapiro		mailerr("550 5.1.1", "%s: User missing home directory", name);
108290795Sgshapiro		return;
108390795Sgshapiro	}
108490795Sgshapiro	else if (sm_snprintf(path, sizeof(path), "%s/%s",
108590795Sgshapiro			     user.mbdb_homedir, HomeMailFile) >= sizeof(path))
108690795Sgshapiro	{
108790795Sgshapiro		exitval = EX_UNAVAILABLE;
108890795Sgshapiro		mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
108990795Sgshapiro		return;
109090795Sgshapiro	}
109164565Sgshapiro
109264565Sgshapiro
109338032Speter	/*
109464565Sgshapiro	**  If the mailbox is linked or a symlink, fail.  There's an obvious
109564565Sgshapiro	**  race here, that the file was replaced with a symbolic link after
109664565Sgshapiro	**  the lstat returned, but before the open.  We attempt to detect
109764565Sgshapiro	**  this by comparing the original stat information and information
109864565Sgshapiro	**  returned by an fstat of the file descriptor returned by the open.
109964565Sgshapiro	**
110064565Sgshapiro	**  NB: this is a symptom of a larger problem, that the mail spooling
110164565Sgshapiro	**  directory is writeable by the wrong users.  If that directory is
110264565Sgshapiro	**  writeable, system security is compromised for other reasons, and
110364565Sgshapiro	**  it cannot be fixed here.
110464565Sgshapiro	**
110564565Sgshapiro	**  If we created the mailbox, set the owner/group.  If that fails,
110664565Sgshapiro	**  just return.  Another process may have already opened it, so we
110764565Sgshapiro	**  can't unlink it.  Historically, binmail set the owner/group at
110864565Sgshapiro	**  each mail delivery.  We no longer do this, assuming that if the
110964565Sgshapiro	**  ownership or permissions were changed there was a reason.
111064565Sgshapiro	**
111164565Sgshapiro	**  XXX
111264565Sgshapiro	**  open(2) should support flock'ing the file.
111364565Sgshapiro	*/
111464565Sgshapiro
111538032Spetertryagain:
111664565Sgshapiro#ifdef MAILLOCK
111764565Sgshapiro	p = name;
1118363466Sgshapiro#else
111964565Sgshapiro	p = path;
1120363466Sgshapiro#endif
112164565Sgshapiro	if ((off = lockmbox(p)) != 0)
112264565Sgshapiro	{
112364565Sgshapiro		if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL)
112464565Sgshapiro		{
112564565Sgshapiro			ExitVal = EX_TEMPFAIL;
112673191Sgshapiro			errcode = "451 4.3.0";
112764565Sgshapiro		}
112864565Sgshapiro		else
112973191Sgshapiro			errcode = "551 5.3.0";
113073191Sgshapiro
113173191Sgshapiro		mailerr(errcode, "lockmailbox %s failed; error code %d %s",
113290795Sgshapiro			p, off, errno > 0 ? sm_errstring(errno) : "");
113364565Sgshapiro		return;
113464565Sgshapiro	}
113564565Sgshapiro
113664565Sgshapiro	if (lstat(path, &sb) < 0)
113764565Sgshapiro	{
113864565Sgshapiro		int save_errno;
113964565Sgshapiro		int mode = S_IRUSR|S_IWUSR;
114090795Sgshapiro		gid_t gid = user.mbdb_gid;
114164565Sgshapiro
114264565Sgshapiro#ifdef MAILGID
114364565Sgshapiro		(void) umask(0007);
114464565Sgshapiro		gid = MAILGID;
114564565Sgshapiro		mode |= S_IRGRP|S_IWGRP;
1146363466Sgshapiro#endif
114764565Sgshapiro
1148120259Sgshapiro		mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY,
114966497Sgshapiro			    mode);
115064565Sgshapiro		save_errno = errno;
115164565Sgshapiro
115238032Speter		if (lstat(path, &sb) < 0)
115338032Speter		{
115464565Sgshapiro			ExitVal = EX_CANTCREAT;
115542580Speter			mailerr("550 5.2.0",
115642580Speter				"%s: lstat: file changed after open", path);
115738032Speter			goto err1;
115838032Speter		}
115973191Sgshapiro		if (mbfd < 0)
116064565Sgshapiro		{
116164565Sgshapiro			if (save_errno == EEXIST)
116238032Speter				goto tryagain;
116366497Sgshapiro
116466497Sgshapiro			/* open failed, don't try again */
116566497Sgshapiro			mailerr("450 4.2.0", "%s: %s", path,
116690795Sgshapiro				sm_errstring(save_errno));
116766497Sgshapiro			goto err0;
116864565Sgshapiro		}
116990795Sgshapiro		else if (fchown(mbfd, user.mbdb_uid, gid) < 0)
117064565Sgshapiro		{
117138032Speter			mailerr("451 4.3.0", "chown %u.%u: %s",
117290795Sgshapiro				user.mbdb_uid, gid, name);
117338032Speter			goto err1;
117438032Speter		}
117566497Sgshapiro		else
117666497Sgshapiro		{
117766497Sgshapiro			/*
117866497Sgshapiro			**  open() was successful, now close it so can
117966497Sgshapiro			**  be opened as the right owner again.
118066497Sgshapiro			**  Paranoia: reset mbdf since the file descriptor
118166497Sgshapiro			**  is no longer valid; better safe than sorry.
118266497Sgshapiro			*/
118366497Sgshapiro
118490795Sgshapiro			sb.st_uid = user.mbdb_uid;
118566497Sgshapiro			(void) close(mbfd);
118666497Sgshapiro			mbfd = -1;
118766497Sgshapiro		}
118864565Sgshapiro	}
1189111826Sgshapiro	else if (sb.st_nlink != 1)
119064565Sgshapiro	{
1191111826Sgshapiro		mailerr("550 5.2.0", "%s: too many links", path);
1192111826Sgshapiro		goto err0;
1193111826Sgshapiro	}
1194111826Sgshapiro	else if (!S_ISREG(sb.st_mode))
1195111826Sgshapiro	{
119638032Speter		mailerr("550 5.2.0", "%s: irregular file", path);
119738032Speter		goto err0;
119864565Sgshapiro	}
119990795Sgshapiro	else if (sb.st_uid != user.mbdb_uid)
120064565Sgshapiro	{
120164565Sgshapiro		ExitVal = EX_CANTCREAT;
120238032Speter		mailerr("550 5.2.0", "%s: wrong ownership (%d)",
120390795Sgshapiro			path, (int) sb.st_uid);
120438032Speter		goto err0;
120564565Sgshapiro	}
120638032Speter
120766497Sgshapiro	/* change UID for quota checks */
120890795Sgshapiro	if (setreuid(0, user.mbdb_uid) < 0)
120964565Sgshapiro	{
121066497Sgshapiro		mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
121190795Sgshapiro			(int) user.mbdb_uid, sm_errstring(errno),
121290795Sgshapiro			(int) getuid(), (int) geteuid());
121366497Sgshapiro		goto err1;
121466497Sgshapiro	}
121566497Sgshapiro#ifdef DEBUG
121690795Sgshapiro	fprintf(stderr, "new euid = %d\n", (int) geteuid());
1217363466Sgshapiro#endif
1218120259Sgshapiro	mbfd = open(path, O_APPEND|O_WRONLY, 0);
121966497Sgshapiro	if (mbfd < 0)
122066497Sgshapiro	{
122190795Sgshapiro		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
122238032Speter		goto err0;
122364565Sgshapiro	}
122464565Sgshapiro	else if (fstat(mbfd, &fsb) < 0 ||
122564565Sgshapiro		 fsb.st_nlink != 1 ||
122664565Sgshapiro		 sb.st_nlink != 1 ||
122764565Sgshapiro		 !S_ISREG(fsb.st_mode) ||
122864565Sgshapiro		 sb.st_dev != fsb.st_dev ||
122964565Sgshapiro		 sb.st_ino != fsb.st_ino ||
123066497Sgshapiro# if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
123164565Sgshapiro		 sb.st_gen != fsb.st_gen ||
1232363466Sgshapiro# endif
123364565Sgshapiro		 sb.st_uid != fsb.st_uid)
123464565Sgshapiro	{
123564565Sgshapiro		ExitVal = EX_TEMPFAIL;
123642580Speter		mailerr("550 5.2.0", "%s: fstat: file changed after open",
123742580Speter			path);
123838032Speter		goto err1;
123938032Speter	}
124038032Speter
124190795Sgshapiro#if 0
124290795Sgshapiro	/*
124390795Sgshapiro	**  This code could be reused if we decide to add a
124490795Sgshapiro	**  per-user quota field to the sm_mbdb interface.
124590795Sgshapiro	*/
124664565Sgshapiro
124790795Sgshapiro	/*
124890795Sgshapiro	**  Fail if the user has a quota specified, and delivery of this
124990795Sgshapiro	**  message would exceed that quota.  We bounce such failures using
125090795Sgshapiro	**  EX_UNAVAILABLE, unless there were internal problems, since
125190795Sgshapiro	**  storing immense messages for later retries can cause queueing
125290795Sgshapiro	**  issues.
125390795Sgshapiro	*/
125490795Sgshapiro
125590795Sgshapiro	if (ui.quota > 0)
125690795Sgshapiro	{
125790795Sgshapiro		struct stat dsb;
125890795Sgshapiro
125990795Sgshapiro		if (fstat(fd, &dsb) < 0)
126090795Sgshapiro		{
126190795Sgshapiro			ExitVal = EX_TEMPFAIL;
126290795Sgshapiro			mailerr("451 4.3.0",
126390795Sgshapiro				"%s: fstat: can't stat temporary storage: %s",
126490795Sgshapiro				ui.mailspool, sm_errstring(errno));
126590795Sgshapiro			goto err1;
126690795Sgshapiro		}
126790795Sgshapiro
126890795Sgshapiro		if (dsb.st_size + sb.st_size + 1 > ui.quota)
126990795Sgshapiro		{
127090795Sgshapiro			ExitVal = EX_UNAVAILABLE;
127190795Sgshapiro			mailerr("551 5.2.2",
127290795Sgshapiro				"%s: Mailbox full or quota exceeded",
127390795Sgshapiro				ui.mailspool);
127490795Sgshapiro			goto err1;
127590795Sgshapiro		}
127690795Sgshapiro	}
127790795Sgshapiro#endif /* 0 */
127890795Sgshapiro
127938032Speter	/* Wait until we can get a lock on the file. */
128064565Sgshapiro	if (flock(mbfd, LOCK_EX) < 0)
128164565Sgshapiro	{
128290795Sgshapiro		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
128338032Speter		goto err1;
128438032Speter	}
128538032Speter
1286112813Sgshapiro	/* Get the starting offset of the new message */
1287110553Sgshapiro	curoff = lseek(mbfd, (off_t) 0, SEEK_END);
1288110553Sgshapiro
128964565Sgshapiro	if (!nobiff)
129064565Sgshapiro	{
129190795Sgshapiro		(void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n",
129290795Sgshapiro				   name, (LONGLONG_T) curoff);
129338084Speter	}
129438032Speter
129538032Speter	/* Copy the message into the file. */
129673191Sgshapiro	if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1)
129764565Sgshapiro	{
129873191Sgshapiro		mailerr("450 4.2.0", "Temporary file: %s",
129990795Sgshapiro			sm_errstring(errno));
130038032Speter		goto err1;
130138032Speter	}
130238032Speter#ifdef DEBUG
130390795Sgshapiro	fprintf(stderr, "before writing: euid = %d\n", (int) geteuid());
1304363466Sgshapiro#endif
130564565Sgshapiro#ifdef CONTENTLENGTH
130664565Sgshapiro	headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ;
130764565Sgshapiro	for (;;)
130864565Sgshapiro	{
130964565Sgshapiro		if (headerbytes == 0)
131064565Sgshapiro		{
131190795Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr);
131264565Sgshapiro			nr = strlen(buf);
131364565Sgshapiro			headerbytes = -1;
131464565Sgshapiro			readamount = 0;
131564565Sgshapiro		}
131664565Sgshapiro		else if (headerbytes > sizeof(buf) || headerbytes < 0)
131764565Sgshapiro			readamount = sizeof(buf);
131864565Sgshapiro		else
131964565Sgshapiro			readamount = headerbytes;
132064565Sgshapiro		if (readamount != 0)
132164565Sgshapiro			nr = read(fd, buf, readamount);
132264565Sgshapiro		if (nr <= 0)
132364565Sgshapiro			break;
132464565Sgshapiro		if (headerbytes > 0)
132564565Sgshapiro			headerbytes -= nr ;
132664565Sgshapiro
132764565Sgshapiro#else /* CONTENTLENGTH */
132838032Speter	while ((nr = read(fd, buf, sizeof(buf))) > 0)
132964565Sgshapiro	{
133064565Sgshapiro#endif /* CONTENTLENGTH */
133138032Speter		for (off = 0; off < nr; off += nw)
133264565Sgshapiro		{
133364565Sgshapiro			if ((nw = write(mbfd, buf + off, nr - off)) < 0)
133464565Sgshapiro			{
133573191Sgshapiro				errcode = "450 4.2.0";
133664565Sgshapiro#ifdef EDQUOT
133773191Sgshapiro				if (errno == EDQUOT && BounceQuota)
133873191Sgshapiro					errcode = "552 5.2.2";
1339363466Sgshapiro#endif
134073191Sgshapiro				mailerr(errcode, "%s: %s",
134190795Sgshapiro					path, sm_errstring(errno));
134238032Speter				goto err3;
134338032Speter			}
134464565Sgshapiro		}
134564565Sgshapiro	}
134664565Sgshapiro	if (nr < 0)
134764565Sgshapiro	{
134873191Sgshapiro		mailerr("450 4.2.0", "Temporary file: %s",
134990795Sgshapiro			sm_errstring(errno));
135038032Speter		goto err3;
135138032Speter	}
135238032Speter
135338032Speter	/* Flush to disk, don't wait for update. */
135464565Sgshapiro	if (!nofsync && fsync(mbfd) < 0)
135564565Sgshapiro	{
135690795Sgshapiro		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
135738032Spetererr3:
135838032Speter#ifdef DEBUG
135990795Sgshapiro		fprintf(stderr, "reset euid = %d\n", (int) geteuid());
1360363466Sgshapiro#endif
1361110563Sgshapiro		if (mbfd >= 0)
1362110563Sgshapiro			(void) ftruncate(mbfd, curoff);
136366497Sgshapiroerr1:		if (mbfd >= 0)
136466497Sgshapiro			(void) close(mbfd);
1365120259Sgshapiroerr0:		(void) setreuid(0, 0);
1366120259Sgshapiro		unlockmbox();
136738032Speter		return;
136838032Speter	}
136938032Speter
1370120259Sgshapiro	/*
1371120259Sgshapiro	**  Save the current size so if the close() fails below
1372120259Sgshapiro	**  we can make sure no other process has changed the mailbox
1373120259Sgshapiro	**  between the failed close and the re-open()/re-lock().
1374120259Sgshapiro	**  If something else has changed the size, we shouldn't
1375120259Sgshapiro	**  try to truncate it as we may do more harm then good
1376120259Sgshapiro	**  (e.g., truncate a later message delivery).
1377120259Sgshapiro	*/
1378120259Sgshapiro
1379120259Sgshapiro	if (fstat(mbfd, &sb) < 0)
1380120259Sgshapiro		cursize = 0;
1381120259Sgshapiro	else
1382120259Sgshapiro		cursize = sb.st_size;
1383120259Sgshapiro
1384120259Sgshapiro
138538032Speter	/* Close and check -- NFS doesn't write until the close. */
138664565Sgshapiro	if (close(mbfd))
138764565Sgshapiro	{
138873191Sgshapiro		errcode = "450 4.2.0";
138964565Sgshapiro#ifdef EDQUOT
139073191Sgshapiro		if (errno == EDQUOT && BounceQuota)
139173191Sgshapiro			errcode = "552 5.2.2";
1392363466Sgshapiro#endif
139390795Sgshapiro		mailerr(errcode, "%s: %s", path, sm_errstring(errno));
1394120259Sgshapiro		mbfd = open(path, O_WRONLY, 0);
1395120259Sgshapiro		if (mbfd < 0 ||
1396120259Sgshapiro		    cursize == 0
1397120259Sgshapiro		    || flock(mbfd, LOCK_EX) < 0 ||
1398120259Sgshapiro		    fstat(mbfd, &sb) < 0 ||
1399120259Sgshapiro		    sb.st_size != cursize ||
1400110563Sgshapiro		    sb.st_nlink != 1 ||
1401110563Sgshapiro		    !S_ISREG(sb.st_mode) ||
1402110563Sgshapiro		    sb.st_dev != fsb.st_dev ||
1403110563Sgshapiro		    sb.st_ino != fsb.st_ino ||
1404110563Sgshapiro# if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
1405110563Sgshapiro		    sb.st_gen != fsb.st_gen ||
1406363466Sgshapiro# endif
1407110563Sgshapiro		    sb.st_uid != fsb.st_uid
1408110563Sgshapiro		   )
1409110563Sgshapiro		{
1410110563Sgshapiro			/* Don't use a bogus file */
1411110563Sgshapiro			if (mbfd >= 0)
1412110563Sgshapiro			{
1413110563Sgshapiro				(void) close(mbfd);
1414110563Sgshapiro				mbfd = -1;
1415110563Sgshapiro			}
1416110563Sgshapiro		}
1417110563Sgshapiro
1418110563Sgshapiro		/* Attempt to truncate back to pre-write size */
1419110563Sgshapiro		goto err3;
142064565Sgshapiro	}
142164565Sgshapiro	else if (!nobiff)
142238032Speter		notifybiff(biffmsg);
142338032Speter
142464565Sgshapiro	if (setreuid(0, 0) < 0)
142564565Sgshapiro	{
142638032Speter		mailerr("450 4.2.0", "setreuid(0, 0): %s",
142790795Sgshapiro			sm_errstring(errno));
142838032Speter		goto err0;
142938032Speter	}
143038032Speter#ifdef DEBUG
143190795Sgshapiro	fprintf(stderr, "reset euid = %d\n", (int) geteuid());
1432363466Sgshapiro#endif
143338032Speter	unlockmbox();
143464565Sgshapiro	if (LMTPMode)
143573191Sgshapiro		printf("250 2.1.5 %s Ok\r\n", name);
143638032Speter}
143738032Speter
143838032Speter/*
143964565Sgshapiro**  user.lock files are necessary for compatibility with other
144064565Sgshapiro**  systems, e.g., when the mail spool file is NFS exported.
144164565Sgshapiro**  Alas, mailbox locking is more than just a local matter.
144264565Sgshapiro**  EPA 11/94.
144364565Sgshapiro*/
144438032Speter
144590795Sgshapirobool	Locked = false;
144638032Speter
144764565Sgshapiro#ifdef MAILLOCK
144864565Sgshapiroint
144964565Sgshapirolockmbox(name)
145064565Sgshapiro	char *name;
145164565Sgshapiro{
145266497Sgshapiro	int r = 0;
145364565Sgshapiro
145464565Sgshapiro	if (Locked)
145564565Sgshapiro		return 0;
145664565Sgshapiro	if ((r = maillock(name, 15)) == L_SUCCESS)
145764565Sgshapiro	{
145890795Sgshapiro		Locked = true;
145964565Sgshapiro		return 0;
146064565Sgshapiro	}
146164565Sgshapiro	switch (r)
146264565Sgshapiro	{
146364565Sgshapiro	  case L_TMPLOCK:	/* Can't create tmp file */
146464565Sgshapiro	  case L_TMPWRITE:	/* Can't write pid into lockfile */
146564565Sgshapiro	  case L_MAXTRYS:	/* Failed after retrycnt attempts */
146664565Sgshapiro		errno = 0;
146764565Sgshapiro		r = EX_TEMPFAIL;
146864565Sgshapiro		break;
146964565Sgshapiro	  case L_ERROR:		/* Check errno for reason */
147064565Sgshapiro		r = errno;
147164565Sgshapiro		break;
147264565Sgshapiro	  default:		/* other permanent errors */
147364565Sgshapiro		errno = 0;
147464565Sgshapiro		r = EX_UNAVAILABLE;
147564565Sgshapiro		break;
147664565Sgshapiro	}
147764565Sgshapiro	return r;
147864565Sgshapiro}
147964565Sgshapiro
148038032Spetervoid
148164565Sgshapirounlockmbox()
148264565Sgshapiro{
148364565Sgshapiro	if (Locked)
148464565Sgshapiro		mailunlock();
148590795Sgshapiro	Locked = false;
148664565Sgshapiro}
148764565Sgshapiro#else /* MAILLOCK */
148864565Sgshapiro
148964565Sgshapirochar	LockName[MAXPATHLEN];
149064565Sgshapiro
149164565Sgshapiroint
149238032Speterlockmbox(path)
149338032Speter	char *path;
149438032Speter{
149538032Speter	int statfailed = 0;
149664565Sgshapiro	time_t start;
149738032Speter
149864565Sgshapiro	if (Locked)
149964565Sgshapiro		return 0;
150064565Sgshapiro	if (strlen(path) + 6 > sizeof LockName)
150164565Sgshapiro		return EX_SOFTWARE;
150290795Sgshapiro	(void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path);
150364565Sgshapiro	(void) time(&start);
150464565Sgshapiro	for (; ; sleep(5))
150564565Sgshapiro	{
150638032Speter		int fd;
150738032Speter		struct stat st;
150838032Speter		time_t now;
150938032Speter
151064565Sgshapiro		/* global timeout */
151164565Sgshapiro		(void) time(&now);
151264565Sgshapiro		if (now > start + LOCKTO_GLOB)
151364565Sgshapiro		{
151464565Sgshapiro			errno = 0;
151564565Sgshapiro			return EX_TEMPFAIL;
151664565Sgshapiro		}
1517120259Sgshapiro		fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, LOCKFILE_PMODE);
151864565Sgshapiro		if (fd >= 0)
151964565Sgshapiro		{
152038032Speter			/* defeat lock checking programs which test pid */
152164565Sgshapiro			(void) write(fd, "0", 2);
152290795Sgshapiro			Locked = true;
152364565Sgshapiro			(void) close(fd);
152464565Sgshapiro			return 0;
152538032Speter		}
152664565Sgshapiro		if (stat(LockName, &st) < 0)
152764565Sgshapiro		{
152838032Speter			if (statfailed++ > 5)
152964565Sgshapiro			{
153064565Sgshapiro				errno = 0;
153164565Sgshapiro				return EX_TEMPFAIL;
153264565Sgshapiro			}
153338032Speter			continue;
153438032Speter		}
153538032Speter		statfailed = 0;
153664565Sgshapiro		(void) time(&now);
153764565Sgshapiro		if (now < st.st_ctime + LOCKTO_RM)
153838032Speter			continue;
153964565Sgshapiro
154064565Sgshapiro		/* try to remove stale lockfile */
154164565Sgshapiro		if (unlink(LockName) < 0)
154264565Sgshapiro			return errno;
154338032Speter	}
154438032Speter}
154538032Speter
154638032Spetervoid
154738032Speterunlockmbox()
154838032Speter{
154964565Sgshapiro	if (!Locked)
155038032Speter		return;
155164565Sgshapiro	(void) unlink(LockName);
155290795Sgshapiro	Locked = false;
155338032Speter}
155464565Sgshapiro#endif /* MAILLOCK */
155538032Speter
155638032Spetervoid
155738032Speternotifybiff(msg)
155838032Speter	char *msg;
155938032Speter{
156090795Sgshapiro	static bool initialized = false;
156138032Speter	static int f = -1;
156238032Speter	struct hostent *hp;
156338032Speter	struct servent *sp;
156438032Speter	int len;
156564565Sgshapiro	static struct sockaddr_in addr;
156638032Speter
156764565Sgshapiro	if (!initialized)
156864565Sgshapiro	{
156990795Sgshapiro		initialized = true;
157064565Sgshapiro
157138032Speter		/* Be silent if biff service not available. */
157264565Sgshapiro		if ((sp = getservbyname("biff", "udp")) == NULL ||
157364565Sgshapiro		    (hp = gethostbyname("localhost")) == NULL ||
157464565Sgshapiro		    hp->h_length != INADDRSZ)
157538032Speter			return;
157664565Sgshapiro
157738032Speter		addr.sin_family = hp->h_addrtype;
157864565Sgshapiro		memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ);
157938032Speter		addr.sin_port = sp->s_port;
158038032Speter	}
158164565Sgshapiro
158264565Sgshapiro	/* No message, just return */
158364565Sgshapiro	if (msg == NULL)
158438032Speter		return;
158564565Sgshapiro
158664565Sgshapiro	/* Couldn't initialize addr struct */
158764565Sgshapiro	if (addr.sin_family == AF_UNSPEC)
158864565Sgshapiro		return;
158964565Sgshapiro
159073191Sgshapiro	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
159164565Sgshapiro		return;
159238032Speter	len = strlen(msg) + 1;
159364565Sgshapiro	(void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr));
159438032Speter}
159538032Speter
159638032Spetervoid
159738032Speterusage()
159838032Speter{
159964565Sgshapiro	ExitVal = EX_USAGE;
1600182352Sgshapiro# if _FFR_SPOOL_PATH
1601182352Sgshapiro	mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] [-p path] user ...");
1602363466Sgshapiro# else
160390795Sgshapiro	mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ...");
1604363466Sgshapiro# endif
1605141862Sgshapiro	sm_exit(ExitVal);
160638032Speter}
160738032Speter
160838032Spetervoid
160990795Sgshapiro/*VARARGS2*/
161038032Speter#ifdef __STDC__
161138032Spetermailerr(const char *hdr, const char *fmt, ...)
161264565Sgshapiro#else /* __STDC__ */
161338032Spetermailerr(hdr, fmt, va_alist)
161438032Speter	const char *hdr;
161538032Speter	const char *fmt;
161638032Speter	va_dcl
161764565Sgshapiro#endif /* __STDC__ */
161838032Speter{
161973191Sgshapiro	size_t len = 0;
162090795Sgshapiro	SM_VA_LOCAL_DECL
162138032Speter
162273191Sgshapiro	(void) e_to_sys(errno);
162373191Sgshapiro
162490795Sgshapiro	SM_VA_START(ap, fmt);
162573191Sgshapiro
162690795Sgshapiro	if (LMTPMode && hdr != NULL)
162738032Speter	{
162890795Sgshapiro		sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr);
162990795Sgshapiro		len = strlen(ErrBuf);
163038032Speter	}
163190795Sgshapiro	(void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap);
163290795Sgshapiro	SM_VA_END(ap);
163373191Sgshapiro
163473191Sgshapiro	if (!HoldErrs)
163573191Sgshapiro		flush_error();
163673191Sgshapiro
163773191Sgshapiro	/* Log the message to syslog. */
163873191Sgshapiro	if (!LMTPMode)
163973191Sgshapiro		syslog(LOG_ERR, "%s", ErrBuf);
164038032Speter}
164138032Speter
164238032Spetervoid
164373191Sgshapiroflush_error()
164438032Speter{
164573191Sgshapiro	if (LMTPMode)
164673191Sgshapiro		printf("%s\r\n", ErrBuf);
164773191Sgshapiro	else
164838032Speter	{
164973191Sgshapiro		if (ExitVal != EX_USAGE)
165073191Sgshapiro			(void) fprintf(stderr, "mail.local: ");
165173191Sgshapiro		fprintf(stderr, "%s\n", ErrBuf);
165238032Speter	}
165338032Speter}
165438032Speter
1655132946Sgshapiro#if HASHSPOOL
1656132946Sgshapiroconst char *
1657132946Sgshapirohashname(name)
1658132946Sgshapiro	char *name;
1659132946Sgshapiro{
1660132946Sgshapiro	static char p[MAXPATHLEN];
1661132946Sgshapiro	int i;
1662132946Sgshapiro	int len;
1663132946Sgshapiro	char *str;
1664132946Sgshapiro# if HASHSPOOLMD5
1665132946Sgshapiro	char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_";
1666132946Sgshapiro	MD5_CTX ctx;
1667132946Sgshapiro	unsigned char md5[18];
1668132946Sgshapiro#  if MAXPATHLEN <= 24
1669132946Sgshapiro    ERROR _MAXPATHLEN <= 24
1670363466Sgshapiro#  endif
1671132946Sgshapiro	char b64[24];
1672132946Sgshapiro	MD5_LONG bits;
1673132946Sgshapiro	int j;
1674132946Sgshapiro# endif /* HASHSPOOLMD5 */
1675132946Sgshapiro
1676132946Sgshapiro	if (HashType == HASH_NONE || HashDepth * 2 >= MAXPATHLEN)
1677132946Sgshapiro	{
1678132946Sgshapiro		p[0] = '\0';
1679132946Sgshapiro		return p;
1680132946Sgshapiro	}
1681132946Sgshapiro
1682132946Sgshapiro	switch(HashType)
1683132946Sgshapiro	{
1684132946Sgshapiro	  case HASH_USER:
1685132946Sgshapiro		str = name;
1686132946Sgshapiro		break;
1687132946Sgshapiro
1688132946Sgshapiro# if HASHSPOOLMD5
1689132946Sgshapiro	  case HASH_MD5:
1690132946Sgshapiro		MD5_Init(&ctx);
1691132946Sgshapiro		MD5_Update(&ctx, name, strlen(name));
1692132946Sgshapiro		MD5_Final(md5, &ctx);
1693132946Sgshapiro		md5[16] = 0;
1694132946Sgshapiro		md5[17] = 0;
1695132946Sgshapiro
1696132946Sgshapiro		for (i = 0; i < 6; i++)
1697132946Sgshapiro		{
1698132946Sgshapiro			bits = (unsigned) md5[(3 * i)] << 16;
1699132946Sgshapiro			bits |= (unsigned) md5[(3 * i) + 1] << 8;
1700132946Sgshapiro			bits |= (unsigned) md5[(3 * i) + 2];
1701132946Sgshapiro
1702132946Sgshapiro			for (j = 3; j >= 0; j--)
1703132946Sgshapiro			{
1704132946Sgshapiro				b64[(4 * i) + j] = Base64[(bits & 0x3f)];
1705132946Sgshapiro				bits >>= 6;
1706132946Sgshapiro			}
1707132946Sgshapiro		}
1708132946Sgshapiro		b64[22] = '\0';
1709132946Sgshapiro		str = b64;
1710132946Sgshapiro		break;
1711132946Sgshapiro# endif /* HASHSPOOLMD5 */
1712132946Sgshapiro	}
1713132946Sgshapiro
1714132946Sgshapiro	len = strlen(str);
1715132946Sgshapiro	for (i = 0; i < HashDepth; i++)
1716132946Sgshapiro	{
1717132946Sgshapiro		if (i < len)
1718132946Sgshapiro			p[i * 2] = str[i];
1719132946Sgshapiro		else
1720132946Sgshapiro			p[i * 2] = '_';
1721132946Sgshapiro		p[(i * 2) + 1] = '/';
1722132946Sgshapiro	}
1723132946Sgshapiro	p[HashDepth * 2] = '\0';
1724132946Sgshapiro	return p;
1725132946Sgshapiro}
1726132946Sgshapiro#endif /* HASHSPOOL */
1727132946Sgshapiro
172838032Speter/*
172938032Speter * e_to_sys --
173038032Speter *	Guess which errno's are temporary.  Gag me.
173138032Speter */
173273191Sgshapiro
173364565Sgshapiroint
173438032Spetere_to_sys(num)
173538032Speter	int num;
173638032Speter{
173738032Speter	/* Temporary failures override hard errors. */
173864565Sgshapiro	if (ExitVal == EX_TEMPFAIL)
173964565Sgshapiro		return ExitVal;
174038032Speter
174164565Sgshapiro	switch (num)		/* Hopefully temporary errors. */
174264565Sgshapiro	{
174364565Sgshapiro#ifdef EDQUOT
174464565Sgshapiro	  case EDQUOT:		/* Disc quota exceeded */
174573191Sgshapiro		if (BounceQuota)
174664565Sgshapiro		{
174764565Sgshapiro			ExitVal = EX_UNAVAILABLE;
174864565Sgshapiro			break;
174964565Sgshapiro		}
175064565Sgshapiro		/* FALLTHROUGH */
175164565Sgshapiro#endif /* EDQUOT */
175238032Speter#ifdef EAGAIN
175364565Sgshapiro	  case EAGAIN:		/* Resource temporarily unavailable */
1754363466Sgshapiro#endif
175538032Speter#ifdef EBUSY
175664565Sgshapiro	  case EBUSY:		/* Device busy */
1757363466Sgshapiro#endif
175838032Speter#ifdef EPROCLIM
175964565Sgshapiro	  case EPROCLIM:	/* Too many processes */
1760363466Sgshapiro#endif
176138032Speter#ifdef EUSERS
176264565Sgshapiro	  case EUSERS:		/* Too many users */
1763363466Sgshapiro#endif
176438032Speter#ifdef ECONNABORTED
176564565Sgshapiro	  case ECONNABORTED:	/* Software caused connection abort */
1766363466Sgshapiro#endif
176738032Speter#ifdef ECONNREFUSED
176864565Sgshapiro	  case ECONNREFUSED:	/* Connection refused */
1769363466Sgshapiro#endif
177038032Speter#ifdef ECONNRESET
177164565Sgshapiro	  case ECONNRESET:	/* Connection reset by peer */
1772363466Sgshapiro#endif
177338032Speter#ifdef EDEADLK
177464565Sgshapiro	  case EDEADLK:		/* Resource deadlock avoided */
1775363466Sgshapiro#endif
177638032Speter#ifdef EFBIG
177764565Sgshapiro	  case EFBIG:		/* File too large */
1778363466Sgshapiro#endif
177938032Speter#ifdef EHOSTDOWN
178064565Sgshapiro	  case EHOSTDOWN:	/* Host is down */
1781363466Sgshapiro#endif
178238032Speter#ifdef EHOSTUNREACH
178364565Sgshapiro	  case EHOSTUNREACH:	/* No route to host */
1784363466Sgshapiro#endif
178538032Speter#ifdef EMFILE
178664565Sgshapiro	  case EMFILE:		/* Too many open files */
1787363466Sgshapiro#endif
178838032Speter#ifdef ENETDOWN
178964565Sgshapiro	  case ENETDOWN:	/* Network is down */
1790363466Sgshapiro#endif
179138032Speter#ifdef ENETRESET
179264565Sgshapiro	  case ENETRESET:	/* Network dropped connection on reset */
1793363466Sgshapiro#endif
179438032Speter#ifdef ENETUNREACH
179564565Sgshapiro	  case ENETUNREACH:	/* Network is unreachable */
1796363466Sgshapiro#endif
179738032Speter#ifdef ENFILE
179864565Sgshapiro	  case ENFILE:		/* Too many open files in system */
1799363466Sgshapiro#endif
180038032Speter#ifdef ENOBUFS
180164565Sgshapiro	  case ENOBUFS:		/* No buffer space available */
1802363466Sgshapiro#endif
180338032Speter#ifdef ENOMEM
180464565Sgshapiro	  case ENOMEM:		/* Cannot allocate memory */
1805363466Sgshapiro#endif
180638032Speter#ifdef ENOSPC
180764565Sgshapiro	  case ENOSPC:		/* No space left on device */
1808363466Sgshapiro#endif
180938032Speter#ifdef EROFS
181064565Sgshapiro	  case EROFS:		/* Read-only file system */
1811363466Sgshapiro#endif
181238032Speter#ifdef ESTALE
181364565Sgshapiro	  case ESTALE:		/* Stale NFS file handle */
1814363466Sgshapiro#endif
181538032Speter#ifdef ETIMEDOUT
181664565Sgshapiro	  case ETIMEDOUT:	/* Connection timed out */
1817363466Sgshapiro#endif
181838032Speter#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
181964565Sgshapiro	  case EWOULDBLOCK:	/* Operation would block. */
1820363466Sgshapiro#endif
182164565Sgshapiro		ExitVal = EX_TEMPFAIL;
182238032Speter		break;
182364565Sgshapiro
182464565Sgshapiro	  default:
182564565Sgshapiro		ExitVal = EX_UNAVAILABLE;
182638032Speter		break;
182738032Speter	}
182864565Sgshapiro	return ExitVal;
182938032Speter}
183038032Speter
183138032Speter#if defined(ultrix) || defined(_CRAY)
183238032Speter/*
183338032Speter * Copyright (c) 1987, 1993
183438032Speter *	The Regents of the University of California.  All rights reserved.
183538032Speter *
183638032Speter * Redistribution and use in source and binary forms, with or without
183738032Speter * modification, are permitted provided that the following conditions
183838032Speter * are met:
183938032Speter * 1. Redistributions of source code must retain the above copyright
184038032Speter *    notice, this list of conditions and the following disclaimer.
184138032Speter * 2. Redistributions in binary form must reproduce the above copyright
184238032Speter *    notice, this list of conditions and the following disclaimer in the
184338032Speter *    documentation and/or other materials provided with the distribution.
184438032Speter * 3. All advertising materials mentioning features or use of this software
184538032Speter *    must display the following acknowledgement:
184638032Speter *	This product includes software developed by the University of
184738032Speter *	California, Berkeley and its contributors.
184838032Speter * 4. Neither the name of the University nor the names of its contributors
184938032Speter *    may be used to endorse or promote products derived from this software
185038032Speter *    without specific prior written permission.
185138032Speter *
185238032Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
185338032Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
185438032Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
185538032Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
185638032Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
185738032Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
185838032Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
185938032Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
186038032Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
186138032Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
186238032Speter * SUCH DAMAGE.
186338032Speter */
186438032Speter
186564565Sgshapiro# if defined(LIBC_SCCS) && !defined(lint)
186638032Speterstatic char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
1867363466Sgshapiro# endif
186838032Speter
186964565Sgshapiro# include <sys/types.h>
187064565Sgshapiro# include <sys/stat.h>
187164565Sgshapiro# include <fcntl.h>
187264565Sgshapiro# include <errno.h>
187364565Sgshapiro# include <stdio.h>
187464565Sgshapiro# include <ctype.h>
187538032Speter
187638032Speterstatic int _gettemp();
187738032Speter
187838032Spetermkstemp(path)
187938032Speter	char *path;
188038032Speter{
188138032Speter	int fd;
188238032Speter
188338032Speter	return (_gettemp(path, &fd) ? fd : -1);
188438032Speter}
188538032Speter
188638032Speterstatic
188738032Speter_gettemp(path, doopen)
188838032Speter	char *path;
188938032Speter	register int *doopen;
189038032Speter{
189138032Speter	extern int errno;
189238032Speter	register char *start, *trv;
189338032Speter	struct stat sbuf;
189490795Sgshapiro	unsigned int pid;
189538032Speter
189638032Speter	pid = getpid();
189738032Speter	for (trv = path; *trv; ++trv);		/* extra X's get set to 0's */
189864565Sgshapiro	while (*--trv == 'X')
189964565Sgshapiro	{
190038032Speter		*trv = (pid % 10) + '0';
190138032Speter		pid /= 10;
190238032Speter	}
190338032Speter
190438032Speter	/*
190538032Speter	 * check the target directory; if you have six X's and it
190638032Speter	 * doesn't exist this runs for a *very* long time.
190738032Speter	 */
190864565Sgshapiro	for (start = trv + 1;; --trv)
190964565Sgshapiro	{
191038032Speter		if (trv <= path)
191138032Speter			break;
191264565Sgshapiro		if (*trv == '/')
191364565Sgshapiro		{
191438032Speter			*trv = '\0';
191538032Speter			if (stat(path, &sbuf) < 0)
191638032Speter				return(0);
191764565Sgshapiro			if (!S_ISDIR(sbuf.st_mode))
191864565Sgshapiro			{
191938032Speter				errno = ENOTDIR;
192038032Speter				return(0);
192138032Speter			}
192238032Speter			*trv = '/';
192338032Speter			break;
192438032Speter		}
192538032Speter	}
192638032Speter
192764565Sgshapiro	for (;;)
192864565Sgshapiro	{
192964565Sgshapiro		if (doopen)
193064565Sgshapiro		{
193166497Sgshapiro			if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR,
193266497Sgshapiro					    0600)) >= 0)
193338032Speter				return(1);
193438032Speter			if (errno != EEXIST)
193538032Speter				return(0);
193638032Speter		}
193738032Speter		else if (stat(path, &sbuf) < 0)
193838032Speter			return(errno == ENOENT ? 1 : 0);
193938032Speter
194038032Speter		/* tricky little algorithm for backward compatibility */
194164565Sgshapiro		for (trv = start;;)
194264565Sgshapiro		{
194338032Speter			if (!*trv)
194438032Speter				return(0);
194538032Speter			if (*trv == 'z')
194638032Speter				*trv++ = 'a';
194764565Sgshapiro			else
194864565Sgshapiro			{
194938032Speter				if (isascii(*trv) && isdigit(*trv))
195038032Speter					*trv = 'a';
195138032Speter				else
195238032Speter					++*trv;
195338032Speter				break;
195438032Speter			}
195538032Speter		}
195638032Speter	}
195764565Sgshapiro	/* NOTREACHED */
195838032Speter}
195964565Sgshapiro#endif /* defined(ultrix) || defined(_CRAY) */
1960