mail.local.c revision 132946
1/*
2 * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1990, 1993, 1994
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 * $FreeBSD: head/contrib/sendmail/mail.local/mail.local.c 132946 2004-08-01 01:16:16Z gshapiro $
12 *
13 */
14
15#include <sm/gen.h>
16
17SM_IDSTR(copyright,
18"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
19	All rights reserved.\n\
20     Copyright (c) 1990, 1993, 1994\n\
21	The Regents of the University of California.  All rights reserved.\n")
22
23SM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.251 2003/11/03 18:38:29 ca Exp $")
24
25#include <stdlib.h>
26#include <sm/errstring.h>
27#include <sm/io.h>
28#include <sm/limits.h>
29# include <unistd.h>
30# ifdef EX_OK
31#  undef EX_OK		/* unistd.h may have another use for this */
32# endif /* EX_OK */
33# define LOCKFILE_PMODE 0
34#include <sm/mbdb.h>
35#include <sm/sysexits.h>
36
37#ifndef HASHSPOOL
38# define HASHSPOOL	0
39#endif /* ! HASHSPOOL */
40#ifndef HASHSPOOLMD5
41# define HASHSPOOLMD5	0
42#endif /* ! HASHSPOOLMD5 */
43
44/*
45**  This is not intended to work on System V derived systems
46**  such as Solaris or HP-UX, since they use a totally different
47**  approach to mailboxes (essentially, they have a set-group-ID program
48**  rather than set-user-ID, and they rely on the ability to "give away"
49**  files to do their work).  IT IS NOT A BUG that this doesn't
50**  work on such architectures.
51*/
52
53
54#include <stdio.h>
55#include <errno.h>
56#include <fcntl.h>
57#include <sys/types.h>
58#include <sys/stat.h>
59#include <time.h>
60#include <stdlib.h>
61# include <sys/socket.h>
62# include <sys/file.h>
63# include <netinet/in.h>
64# include <arpa/nameser.h>
65# include <netdb.h>
66# include <pwd.h>
67
68#include <sm/string.h>
69#include <syslog.h>
70#include <ctype.h>
71
72#include <sm/conf.h>
73#include <sendmail/pathnames.h>
74
75#if HASHSPOOL
76# define HASH_NONE	0
77# define HASH_USER	1
78# if HASHSPOOLMD5
79#  define HASH_MD5	2
80#  include <openssl/md5.h>
81# endif /* HASHSPOOLMD5 */
82#endif /* HASHSPOOL */
83
84
85#ifndef LOCKTO_RM
86# define LOCKTO_RM	300	/* timeout for stale lockfile removal */
87#endif /* ! LOCKTO_RM */
88#ifndef LOCKTO_GLOB
89# define LOCKTO_GLOB	400	/* global timeout for lockfile creation */
90#endif /* ! LOCKTO_GLOB */
91
92/* define a realloc() which works for NULL pointers */
93#define REALLOC(ptr, size)	(((ptr) == NULL) ? malloc(size) : realloc(ptr, size))
94
95/*
96**  If you don't have flock, you could try using lockf instead.
97*/
98
99#ifdef LDA_USE_LOCKF
100# define flock(a, b)	lockf(a, b, 0)
101# ifdef LOCK_EX
102#  undef LOCK_EX
103# endif /* LOCK_EX */
104# define LOCK_EX	F_LOCK
105#endif /* LDA_USE_LOCKF */
106
107#ifndef LOCK_EX
108# include <sys/file.h>
109#endif /* ! LOCK_EX */
110
111/*
112**  If you don't have setreuid, and you have saved uids, and you have
113**  a seteuid() call that doesn't try to emulate using setuid(), then
114**  you can try defining LDA_USE_SETEUID.
115*/
116
117#ifdef LDA_USE_SETEUID
118# define setreuid(r, e)		seteuid(e)
119#endif /* LDA_USE_SETEUID */
120
121#ifdef LDA_CONTENTLENGTH
122# define CONTENTLENGTH	1
123#endif /* LDA_CONTENTLENGTH */
124
125#ifndef INADDRSZ
126# define INADDRSZ	4		/* size of an IPv4 address in bytes */
127#endif /* ! INADDRSZ */
128
129#ifdef MAILLOCK
130# include <maillock.h>
131#endif /* MAILLOCK */
132
133#ifndef MAILER_DAEMON
134# define MAILER_DAEMON	"MAILER-DAEMON"
135#endif /* ! MAILER_DAEMON */
136
137#ifdef CONTENTLENGTH
138char	ContentHdr[40] = "Content-Length: ";
139off_t	HeaderLength;
140off_t	BodyLength;
141#endif /* CONTENTLENGTH */
142
143bool	EightBitMime = true;		/* advertise 8BITMIME in LMTP */
144char	ErrBuf[10240];			/* error buffer */
145int	ExitVal = EX_OK;		/* sysexits.h error value. */
146bool	nobiff = false;
147bool	nofsync = false;
148bool	HoldErrs = false;		/* Hold errors in ErrBuf */
149bool	LMTPMode = false;
150bool	BounceQuota = false;		/* permanent error when over quota */
151char	*HomeMailFile = NULL;		/* store mail in homedir */
152
153#if HASHSPOOL
154int	HashType = HASH_NONE;
155int	HashDepth = 0;
156bool	StripRcptDomain = true;
157#else /* HASHSPOOL */
158# define StripRcptDomain true
159#endif /* HASHSPOOL */
160char	SpoolPath[MAXPATHLEN];
161
162void	deliver __P((int, char *));
163int	e_to_sys __P((int));
164void	notifybiff __P((char *));
165int	store __P((char *, bool *));
166void	usage __P((void));
167int	lockmbox __P((char *));
168void	unlockmbox __P((void));
169void	mailerr __P((const char *, const char *, ...));
170void	flush_error __P((void));
171#if HASHSPOOL
172const char	*hashname __P((char *));
173#endif /* HASHSPOOL */
174
175
176int
177main(argc, argv)
178	int argc;
179	char *argv[];
180{
181	struct passwd *pw;
182	int ch, fd;
183	uid_t uid;
184	char *from;
185	char *mbdbname = "pw";
186	int err;
187	extern char *optarg;
188	extern int optind;
189
190
191	/* make sure we have some open file descriptors */
192	for (fd = 10; fd < 30; fd++)
193		(void) close(fd);
194
195	/* use a reasonable umask */
196	(void) umask(0077);
197
198# ifdef LOG_MAIL
199	openlog("mail.local", 0, LOG_MAIL);
200# else /* LOG_MAIL */
201	openlog("mail.local", 0);
202# endif /* LOG_MAIL */
203
204	from = NULL;
205	if (sm_strlcpy(SpoolPath, _PATH_MAILDIR, sizeof(SpoolPath)) >=
206	    sizeof(SpoolPath))
207	{
208		mailerr("421", "Configuration error: _PATH_MAILDIR too large");
209		exit(EX_CONFIG);
210	}
211#if HASHSPOOL
212	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lH:p:ns")) != -1)
213#else /* HASHSPOOL */
214	while ((ch = getopt(argc, argv, "7BbdD:f:h:r:ls")) != -1)
215#endif /* HASHSPOOL */
216	{
217		switch(ch)
218		{
219		  case '7':		/* Do not advertise 8BITMIME */
220			EightBitMime = false;
221			break;
222
223		  case 'B':
224			nobiff = true;
225			break;
226
227		  case 'b':		/* bounce mail when over quota. */
228			BounceQuota = true;
229			break;
230
231		  case 'd':		/* Backward compatible. */
232			break;
233
234		  case 'D':		/* mailbox database type */
235			mbdbname = optarg;
236			break;
237
238		  case 'f':
239		  case 'r':		/* Backward compatible. */
240			if (from != NULL)
241			{
242				mailerr(NULL, "Multiple -f options");
243				usage();
244			}
245			from = optarg;
246			break;
247
248		  case 'h':
249			if (optarg != NULL || *optarg != '\0')
250				HomeMailFile = optarg;
251			else
252			{
253				mailerr(NULL, "-h: missing filename");
254				usage();
255			}
256			break;
257
258		  case 'l':
259			LMTPMode = true;
260			break;
261
262		  case 's':
263			nofsync++;
264			break;
265
266#if HASHSPOOL
267		  case 'H':
268			if (optarg == NULL || *optarg == '\0')
269			{
270				mailerr(NULL, "-H: missing hashinfo");
271				usage();
272			}
273			switch(optarg[0])
274			{
275			  case 'u':
276				HashType = HASH_USER;
277				break;
278
279# if HASHSPOOLMD5
280			  case 'm':
281				HashType = HASH_MD5;
282				break;
283# endif /* HASHSPOOLMD5 */
284
285			  default:
286				mailerr(NULL, "-H: unknown hash type");
287				usage();
288			}
289			if (optarg[1] == '\0')
290			{
291				mailerr(NULL, "-H: invalid hash depth");
292				usage();
293			}
294			HashDepth = atoi(&optarg[1]);
295			if ((HashDepth <= 0) || ((HashDepth * 2) >= MAXPATHLEN))
296			{
297				mailerr(NULL, "-H: invalid hash depth");
298				usage();
299			}
300			break;
301
302		  case 'p':
303			if (optarg == NULL || *optarg == '\0')
304			{
305				mailerr(NULL, "-p: missing spool path");
306				usage();
307			}
308			if (sm_strlcpy(SpoolPath, optarg, sizeof(SpoolPath)) >=
309			    sizeof(SpoolPath))
310			{
311				mailerr(NULL, "-p: invalid spool path");
312				usage();
313			}
314			break;
315
316		  case 'n':
317			StripRcptDomain = false;
318			break;
319#endif /* HASHSPOOL */
320
321		  case '?':
322		  default:
323			usage();
324		}
325	}
326	argc -= optind;
327	argv += optind;
328
329	/* initialize biff structures */
330	if (!nobiff)
331		notifybiff(NULL);
332
333	err = sm_mbdb_initialize(mbdbname);
334	if (err != EX_OK)
335	{
336		char *errcode = "521";
337
338		if (err == EX_TEMPFAIL)
339			errcode = "421";
340
341		mailerr(errcode, "Can not open mailbox database %s: %s",
342			mbdbname, sm_strexit(err));
343		exit(err);
344	}
345
346	if (LMTPMode)
347	{
348		extern void dolmtp __P((void));
349
350		if (argc > 0)
351		{
352			mailerr("421", "Users should not be specified in command line if LMTP required");
353			exit(EX_TEMPFAIL);
354		}
355
356		dolmtp();
357		/* NOTREACHED */
358		exit(EX_OK);
359	}
360
361	/* Non-LMTP from here on out */
362	if (*argv == '\0')
363		usage();
364
365	/*
366	**  If from not specified, use the name from getlogin() if the
367	**  uid matches, otherwise, use the name from the password file
368	**  corresponding to the uid.
369	*/
370
371	uid = getuid();
372	if (from == NULL && ((from = getlogin()) == NULL ||
373			     (pw = getpwnam(from)) == NULL ||
374			     pw->pw_uid != uid))
375		from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???";
376
377	/*
378	**  There is no way to distinguish the error status of one delivery
379	**  from the rest of the deliveries.  So, if we failed hard on one
380	**  or more deliveries, but had no failures on any of the others, we
381	**  return a hard failure.  If we failed temporarily on one or more
382	**  deliveries, we return a temporary failure regardless of the other
383	**  failures.  This results in the delivery being reattempted later
384	**  at the expense of repeated failures and multiple deliveries.
385	*/
386
387	HoldErrs = true;
388	fd = store(from, NULL);
389	HoldErrs = false;
390	if (fd < 0)
391	{
392		flush_error();
393		exit(ExitVal);
394	}
395	for (; *argv != NULL; ++argv)
396		deliver(fd, *argv);
397	exit(ExitVal);
398	/* NOTREACHED */
399	return ExitVal;
400}
401
402char *
403parseaddr(s, rcpt)
404	char *s;
405	bool rcpt;
406{
407	char *p;
408	int l;
409
410	if (*s++ != '<')
411		return NULL;
412
413	p = s;
414
415	/* at-domain-list */
416	while (*p == '@')
417	{
418		p++;
419		while (*p != ',' && *p != ':' && *p != '\0')
420			p++;
421		if (*p == '\0')
422			return NULL;
423
424		/* Skip over , or : */
425		p++;
426	}
427
428	s = p;
429
430	/* local-part */
431	while (*p != '\0' && *p != '@' && *p != '>')
432	{
433		if (*p == '\\')
434		{
435			if (*++p == '\0')
436				return NULL;
437		}
438		else if (*p == '\"')
439		{
440			p++;
441			while (*p != '\0' && *p != '\"')
442			{
443				if (*p == '\\')
444				{
445					if (*++p == '\0')
446						return NULL;
447				}
448				p++;
449			}
450			if (*p == '\0' || *(p + 1) == '\0')
451				return NULL;
452		}
453		/* +detail ? */
454		if (*p == '+' && rcpt)
455			*p = '\0';
456		p++;
457	}
458
459	/* @domain */
460	if (*p == '@')
461	{
462		if (rcpt)
463			*p++ = '\0';
464		while (*p != '\0' && *p != '>')
465			p++;
466	}
467
468	if (*p != '>')
469		return NULL;
470	else
471		*p = '\0';
472	p++;
473
474	if (*p != '\0' && *p != ' ')
475		return NULL;
476
477	if (*s == '\0')
478		s = MAILER_DAEMON;
479
480	l = strlen(s) + 1;
481	if (l < 0)
482		return NULL;
483	p = malloc(l);
484	if (p == NULL)
485	{
486		mailerr("421 4.3.0", "Memory exhausted");
487		exit(EX_TEMPFAIL);
488	}
489
490	(void) sm_strlcpy(p, s, l);
491	return p;
492}
493
494char *
495process_recipient(addr)
496	char *addr;
497{
498	SM_MBDB_T user;
499
500	switch (sm_mbdb_lookup(addr, &user))
501	{
502	  case EX_OK:
503		return NULL;
504
505	  case EX_NOUSER:
506		return "550 5.1.1 User unknown";
507
508	  case EX_TEMPFAIL:
509		return "451 4.3.0 User database failure; retry later";
510
511	  default:
512		return "550 5.3.0 User database failure";
513	}
514}
515
516#define RCPT_GROW	30
517
518void
519dolmtp()
520{
521	char *return_path = NULL;
522	char **rcpt_addr = NULL;
523	int rcpt_num = 0;
524	int rcpt_alloc = 0;
525	bool gotlhlo = false;
526	char *err;
527	int msgfd;
528	char *p;
529	int i;
530	char myhostname[1024];
531	char buf[4096];
532
533	memset(myhostname, '\0', sizeof myhostname);
534	(void) gethostname(myhostname, sizeof myhostname - 1);
535	if (myhostname[0] == '\0')
536		sm_strlcpy(myhostname, "localhost", sizeof myhostname);
537
538	printf("220 %s LMTP ready\r\n", myhostname);
539	for (;;)
540	{
541		(void) fflush(stdout);
542		if (fgets(buf, sizeof(buf) - 1, stdin) == NULL)
543			exit(EX_OK);
544		p = buf + strlen(buf) - 1;
545		if (p >= buf && *p == '\n')
546			*p-- = '\0';
547		if (p >= buf && *p == '\r')
548			*p-- = '\0';
549
550		switch (buf[0])
551		{
552		  case 'd':
553		  case 'D':
554			if (sm_strcasecmp(buf, "data") == 0)
555			{
556				bool inbody = false;
557
558				if (rcpt_num == 0)
559				{
560					mailerr("503 5.5.1", "No recipients");
561					continue;
562				}
563				HoldErrs = true;
564				msgfd = store(return_path, &inbody);
565				HoldErrs = false;
566				if (msgfd < 0 && !inbody)
567				{
568					flush_error();
569					continue;
570				}
571
572				for (i = 0; i < rcpt_num; i++)
573				{
574					if (msgfd < 0)
575					{
576						/* print error for rcpt */
577						flush_error();
578						continue;
579					}
580					p = strchr(rcpt_addr[i], '+');
581					if (p != NULL)
582						*p = '\0';
583					deliver(msgfd, rcpt_addr[i]);
584				}
585				if (msgfd >= 0)
586					(void) close(msgfd);
587				goto rset;
588			}
589			goto syntaxerr;
590			/* NOTREACHED */
591			break;
592
593		  case 'l':
594		  case 'L':
595			if (sm_strncasecmp(buf, "lhlo ", 5) == 0)
596			{
597				/* check for duplicate per RFC 1651 4.2 */
598				if (gotlhlo)
599				{
600					mailerr("503", "%s Duplicate LHLO",
601					       myhostname);
602					continue;
603				}
604				gotlhlo = true;
605				printf("250-%s\r\n", myhostname);
606				if (EightBitMime)
607					printf("250-8BITMIME\r\n");
608				printf("250-ENHANCEDSTATUSCODES\r\n");
609				printf("250 PIPELINING\r\n");
610				continue;
611			}
612			goto syntaxerr;
613			/* NOTREACHED */
614			break;
615
616		  case 'm':
617		  case 'M':
618			if (sm_strncasecmp(buf, "mail ", 5) == 0)
619			{
620				if (return_path != NULL)
621				{
622					mailerr("503 5.5.1",
623						"Nested MAIL command");
624					continue;
625				}
626				if (sm_strncasecmp(buf + 5, "from:", 5) != 0 ||
627				    ((return_path = parseaddr(buf + 10,
628							      false)) == NULL))
629				{
630					mailerr("501 5.5.4",
631						"Syntax error in parameters");
632					continue;
633				}
634				printf("250 2.5.0 Ok\r\n");
635				continue;
636			}
637			goto syntaxerr;
638			/* NOTREACHED */
639			break;
640
641		  case 'n':
642		  case 'N':
643			if (sm_strcasecmp(buf, "noop") == 0)
644			{
645				printf("250 2.0.0 Ok\r\n");
646				continue;
647			}
648			goto syntaxerr;
649			/* NOTREACHED */
650			break;
651
652		  case 'q':
653		  case 'Q':
654			if (sm_strcasecmp(buf, "quit") == 0)
655			{
656				printf("221 2.0.0 Bye\r\n");
657				exit(EX_OK);
658			}
659			goto syntaxerr;
660			/* NOTREACHED */
661			break;
662
663		  case 'r':
664		  case 'R':
665			if (sm_strncasecmp(buf, "rcpt ", 5) == 0)
666			{
667				if (return_path == NULL)
668				{
669					mailerr("503 5.5.1",
670						"Need MAIL command");
671					continue;
672				}
673				if (rcpt_num >= rcpt_alloc)
674				{
675					rcpt_alloc += RCPT_GROW;
676					rcpt_addr = (char **)
677						REALLOC((char *) rcpt_addr,
678							rcpt_alloc *
679							sizeof(char **));
680					if (rcpt_addr == NULL)
681					{
682						mailerr("421 4.3.0",
683							"Memory exhausted");
684						exit(EX_TEMPFAIL);
685					}
686				}
687				if (sm_strncasecmp(buf + 5, "to:", 3) != 0 ||
688				    ((rcpt_addr[rcpt_num] = parseaddr(buf + 8,
689								      StripRcptDomain)) == NULL))
690				{
691					mailerr("501 5.5.4",
692						"Syntax error in parameters");
693					continue;
694				}
695				err = process_recipient(rcpt_addr[rcpt_num]);
696				if (err != NULL)
697				{
698					mailerr(NULL, "%s", err);
699					continue;
700				}
701				rcpt_num++;
702				printf("250 2.1.5 Ok\r\n");
703				continue;
704			}
705			else if (sm_strcasecmp(buf, "rset") == 0)
706			{
707				printf("250 2.0.0 Ok\r\n");
708
709rset:
710				while (rcpt_num > 0)
711					free(rcpt_addr[--rcpt_num]);
712				if (return_path != NULL)
713					free(return_path);
714				return_path = NULL;
715				continue;
716			}
717			goto syntaxerr;
718			/* NOTREACHED */
719			break;
720
721		  case 'v':
722		  case 'V':
723			if (sm_strncasecmp(buf, "vrfy ", 5) == 0)
724			{
725				printf("252 2.3.3 Try RCPT to attempt delivery\r\n");
726				continue;
727			}
728			goto syntaxerr;
729			/* NOTREACHED */
730			break;
731
732		  default:
733  syntaxerr:
734			mailerr("500 5.5.2", "Syntax error");
735			continue;
736			/* NOTREACHED */
737			break;
738		}
739	}
740}
741
742int
743store(from, inbody)
744	char *from;
745	bool *inbody;
746{
747	FILE *fp = NULL;
748	time_t tval;
749	bool eline;		/* previous line was empty */
750	bool fullline = true;	/* current line is terminated */
751	bool prevfl;		/* previous line was terminated */
752	char line[2048];
753	int fd;
754	char tmpbuf[sizeof _PATH_LOCTMP + 1];
755
756	if (inbody != NULL)
757		*inbody = false;
758
759	(void) umask(0077);
760	(void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf);
761	if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL)
762	{
763		if (fd >= 0)
764			(void) close(fd);
765		mailerr("451 4.3.0", "Unable to open temporary file");
766		return -1;
767	}
768	(void) unlink(tmpbuf);
769
770	if (LMTPMode)
771	{
772		printf("354 Go ahead\r\n");
773		(void) fflush(stdout);
774	}
775	if (inbody != NULL)
776		*inbody = true;
777
778	(void) time(&tval);
779	(void) fprintf(fp, "From %s %s", from, ctime(&tval));
780
781#ifdef CONTENTLENGTH
782	HeaderLength = 0;
783	BodyLength = -1;
784#endif /* CONTENTLENGTH */
785
786	line[0] = '\0';
787	eline = true;
788	while (fgets(line, sizeof(line), stdin) != (char *) NULL)
789	{
790		size_t line_len = 0;
791		int peek;
792
793		prevfl = fullline;	/* preserve state of previous line */
794		while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
795			line_len++;
796		line_len++;
797
798		/* Check for dot-stuffing */
799		if (prevfl && LMTPMode && line[0] == '.')
800		{
801			if (line[1] == '\n' ||
802			    (line[1] == '\r' && line[2] == '\n'))
803				goto lmtpdot;
804			memcpy(line, line + 1, line_len);
805			line_len--;
806		}
807
808		/* Check to see if we have the full line from fgets() */
809		fullline = false;
810		if (line_len > 0)
811		{
812			if (line[line_len - 1] == '\n')
813			{
814				if (line_len >= 2 &&
815				    line[line_len - 2] == '\r')
816				{
817					line[line_len - 2] = '\n';
818					line[line_len - 1] = '\0';
819					line_len--;
820				}
821				fullline = true;
822			}
823			else if (line[line_len - 1] == '\r')
824			{
825				/* Did we just miss the CRLF? */
826				peek = fgetc(stdin);
827				if (peek == '\n')
828				{
829					line[line_len - 1] = '\n';
830					fullline = true;
831				}
832				else
833					(void) ungetc(peek, stdin);
834			}
835		}
836		else
837			fullline = true;
838
839#ifdef CONTENTLENGTH
840		if (prevfl && line[0] == '\n' && HeaderLength == 0)
841		{
842			eline = false;
843			if (fp != NULL)
844				HeaderLength = ftell(fp);
845			if (HeaderLength <= 0)
846			{
847				/*
848				**  shouldn't happen, unless ftell() is
849				**  badly broken
850				*/
851
852				HeaderLength = -1;
853			}
854		}
855#else /* CONTENTLENGTH */
856		if (prevfl && line[0] == '\n')
857			eline = true;
858#endif /* CONTENTLENGTH */
859		else
860		{
861			if (eline && line[0] == 'F' &&
862			    fp != NULL &&
863			    !memcmp(line, "From ", 5))
864				(void) putc('>', fp);
865			eline = false;
866#ifdef CONTENTLENGTH
867			/* discard existing "Content-Length:" headers */
868			if (prevfl && HeaderLength == 0 &&
869			    (line[0] == 'C' || line[0] == 'c') &&
870			    sm_strncasecmp(line, ContentHdr, 15) == 0)
871			{
872				/*
873				**  be paranoid: clear the line
874				**  so no "wrong matches" may occur later
875				*/
876				line[0] = '\0';
877				continue;
878			}
879#endif /* CONTENTLENGTH */
880
881		}
882		if (fp != NULL)
883		{
884			(void) fwrite(line, sizeof(char), line_len, fp);
885			if (ferror(fp))
886			{
887				mailerr("451 4.3.0",
888					"Temporary file write error");
889				(void) fclose(fp);
890				fp = NULL;
891				continue;
892			}
893		}
894	}
895
896	/* check if an error occurred */
897	if (fp == NULL)
898		return -1;
899
900	if (LMTPMode)
901	{
902		/* Got a premature EOF -- toss message and exit */
903		exit(EX_OK);
904	}
905
906	/* If message not newline terminated, need an extra. */
907	if (fp != NULL && strchr(line, '\n') == NULL)
908		(void) putc('\n', fp);
909
910  lmtpdot:
911
912#ifdef CONTENTLENGTH
913	if (fp != NULL)
914		BodyLength = ftell(fp);
915	if (HeaderLength == 0 && BodyLength > 0)	/* empty body */
916	{
917		HeaderLength = BodyLength;
918		BodyLength = 0;
919	}
920	else
921		BodyLength = BodyLength - HeaderLength - 1 ;
922
923	if (HeaderLength > 0 && BodyLength >= 0)
924	{
925		(void) sm_snprintf(line, sizeof line, "%lld\n",
926				   (LONGLONG_T) BodyLength);
927		(void) sm_strlcpy(&ContentHdr[16], line,
928				  sizeof(ContentHdr) - 16);
929	}
930	else
931		BodyLength = -1;	/* Something is wrong here */
932#endif /* CONTENTLENGTH */
933
934	/* Output a newline; note, empty messages are allowed. */
935	if (fp != NULL)
936		(void) putc('\n', fp);
937
938	if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0)
939	{
940		mailerr("451 4.3.0", "Temporary file write error");
941		if (fp != NULL)
942			(void) fclose(fp);
943		return -1;
944	}
945	return fd;
946}
947
948void
949deliver(fd, name)
950	int fd;
951	char *name;
952{
953	struct stat fsb;
954	struct stat sb;
955	char path[MAXPATHLEN];
956	int mbfd = -1, nr = 0, nw, off;
957	int exitval;
958	char *p;
959	char *errcode;
960	off_t curoff, cursize;
961#ifdef CONTENTLENGTH
962	off_t headerbytes;
963	int readamount;
964#endif /* CONTENTLENGTH */
965	char biffmsg[100], buf[8 * 1024];
966	SM_MBDB_T user;
967
968	/*
969	**  Disallow delivery to unknown names -- special mailboxes can be
970	**  handled in the sendmail aliases file.
971	*/
972
973	exitval = sm_mbdb_lookup(name, &user);
974	switch (exitval)
975	{
976	  case EX_OK:
977		break;
978
979	  case EX_NOUSER:
980		exitval = EX_UNAVAILABLE;
981		mailerr("550 5.1.1", "%s: User unknown", name);
982		break;
983
984	  case EX_TEMPFAIL:
985		mailerr("451 4.3.0", "%s: User database failure; retry later",
986			name);
987		break;
988
989	  default:
990		exitval = EX_UNAVAILABLE;
991		mailerr("550 5.3.0", "%s: User database failure", name);
992		break;
993	}
994
995	if (exitval != EX_OK)
996	{
997		if (ExitVal != EX_TEMPFAIL)
998			ExitVal = exitval;
999		return;
1000	}
1001
1002	endpwent();
1003
1004	/*
1005	**  Keep name reasonably short to avoid buffer overruns.
1006	**	This isn't necessary on BSD because of the proper
1007	**	definition of snprintf(), but it can cause problems
1008	**	on other systems.
1009	**  Also, clear out any bogus characters.
1010	*/
1011
1012#if !HASHSPOOL
1013	if (strlen(name) > 40)
1014		name[40] = '\0';
1015	for (p = name; *p != '\0'; p++)
1016	{
1017		if (!isascii(*p))
1018			*p &= 0x7f;
1019		else if (!isprint(*p))
1020			*p = '.';
1021	}
1022#endif /* !HASHSPOOL */
1023
1024
1025	if (HomeMailFile == NULL)
1026	{
1027		if (sm_strlcpyn(path, sizeof(path),
1028#if HASHSPOOL
1029				4,
1030#else /* HASHSPOOL */
1031				3,
1032#endif /* HASHSPOOL */
1033				SpoolPath, "/",
1034#if HASHSPOOL
1035				hashname(name),
1036#endif /* HASHSPOOL */
1037				name) >= sizeof(path))
1038		{
1039			exitval = EX_UNAVAILABLE;
1040			mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
1041			return;
1042		}
1043	}
1044	else if (*user.mbdb_homedir == '\0')
1045	{
1046		exitval = EX_UNAVAILABLE;
1047		mailerr("550 5.1.1", "%s: User missing home directory", name);
1048		return;
1049	}
1050	else if (sm_snprintf(path, sizeof(path), "%s/%s",
1051			     user.mbdb_homedir, HomeMailFile) >= sizeof(path))
1052	{
1053		exitval = EX_UNAVAILABLE;
1054		mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
1055		return;
1056	}
1057
1058
1059	/*
1060	**  If the mailbox is linked or a symlink, fail.  There's an obvious
1061	**  race here, that the file was replaced with a symbolic link after
1062	**  the lstat returned, but before the open.  We attempt to detect
1063	**  this by comparing the original stat information and information
1064	**  returned by an fstat of the file descriptor returned by the open.
1065	**
1066	**  NB: this is a symptom of a larger problem, that the mail spooling
1067	**  directory is writeable by the wrong users.  If that directory is
1068	**  writeable, system security is compromised for other reasons, and
1069	**  it cannot be fixed here.
1070	**
1071	**  If we created the mailbox, set the owner/group.  If that fails,
1072	**  just return.  Another process may have already opened it, so we
1073	**  can't unlink it.  Historically, binmail set the owner/group at
1074	**  each mail delivery.  We no longer do this, assuming that if the
1075	**  ownership or permissions were changed there was a reason.
1076	**
1077	**  XXX
1078	**  open(2) should support flock'ing the file.
1079	*/
1080
1081tryagain:
1082#ifdef MAILLOCK
1083	p = name;
1084#else /* MAILLOCK */
1085	p = path;
1086#endif /* MAILLOCK */
1087	if ((off = lockmbox(p)) != 0)
1088	{
1089		if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL)
1090		{
1091			ExitVal = EX_TEMPFAIL;
1092			errcode = "451 4.3.0";
1093		}
1094		else
1095			errcode = "551 5.3.0";
1096
1097		mailerr(errcode, "lockmailbox %s failed; error code %d %s",
1098			p, off, errno > 0 ? sm_errstring(errno) : "");
1099		return;
1100	}
1101
1102	if (lstat(path, &sb) < 0)
1103	{
1104		int save_errno;
1105		int mode = S_IRUSR|S_IWUSR;
1106		gid_t gid = user.mbdb_gid;
1107
1108#ifdef MAILGID
1109		(void) umask(0007);
1110		gid = MAILGID;
1111		mode |= S_IRGRP|S_IWGRP;
1112#endif /* MAILGID */
1113
1114		mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY,
1115			    mode);
1116		save_errno = errno;
1117
1118		if (lstat(path, &sb) < 0)
1119		{
1120			ExitVal = EX_CANTCREAT;
1121			mailerr("550 5.2.0",
1122				"%s: lstat: file changed after open", path);
1123			goto err1;
1124		}
1125		if (mbfd < 0)
1126		{
1127			if (save_errno == EEXIST)
1128				goto tryagain;
1129
1130			/* open failed, don't try again */
1131			mailerr("450 4.2.0", "%s: %s", path,
1132				sm_errstring(save_errno));
1133			goto err0;
1134		}
1135		else if (fchown(mbfd, user.mbdb_uid, gid) < 0)
1136		{
1137			mailerr("451 4.3.0", "chown %u.%u: %s",
1138				user.mbdb_uid, gid, name);
1139			goto err1;
1140		}
1141		else
1142		{
1143			/*
1144			**  open() was successful, now close it so can
1145			**  be opened as the right owner again.
1146			**  Paranoia: reset mbdf since the file descriptor
1147			**  is no longer valid; better safe than sorry.
1148			*/
1149
1150			sb.st_uid = user.mbdb_uid;
1151			(void) close(mbfd);
1152			mbfd = -1;
1153		}
1154	}
1155	else if (sb.st_nlink != 1)
1156	{
1157		mailerr("550 5.2.0", "%s: too many links", path);
1158		goto err0;
1159	}
1160	else if (!S_ISREG(sb.st_mode))
1161	{
1162		mailerr("550 5.2.0", "%s: irregular file", path);
1163		goto err0;
1164	}
1165	else if (sb.st_uid != user.mbdb_uid)
1166	{
1167		ExitVal = EX_CANTCREAT;
1168		mailerr("550 5.2.0", "%s: wrong ownership (%d)",
1169			path, (int) sb.st_uid);
1170		goto err0;
1171	}
1172
1173	/* change UID for quota checks */
1174	if (setreuid(0, user.mbdb_uid) < 0)
1175	{
1176		mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
1177			(int) user.mbdb_uid, sm_errstring(errno),
1178			(int) getuid(), (int) geteuid());
1179		goto err1;
1180	}
1181#ifdef DEBUG
1182	fprintf(stderr, "new euid = %d\n", (int) geteuid());
1183#endif /* DEBUG */
1184	mbfd = open(path, O_APPEND|O_WRONLY, 0);
1185	if (mbfd < 0)
1186	{
1187		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
1188		goto err0;
1189	}
1190	else if (fstat(mbfd, &fsb) < 0 ||
1191		 fsb.st_nlink != 1 ||
1192		 sb.st_nlink != 1 ||
1193		 !S_ISREG(fsb.st_mode) ||
1194		 sb.st_dev != fsb.st_dev ||
1195		 sb.st_ino != fsb.st_ino ||
1196# if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
1197		 sb.st_gen != fsb.st_gen ||
1198# endif /* HAS_ST_GEN && 0 */
1199		 sb.st_uid != fsb.st_uid)
1200	{
1201		ExitVal = EX_TEMPFAIL;
1202		mailerr("550 5.2.0", "%s: fstat: file changed after open",
1203			path);
1204		goto err1;
1205	}
1206
1207#if 0
1208	/*
1209	**  This code could be reused if we decide to add a
1210	**  per-user quota field to the sm_mbdb interface.
1211	*/
1212
1213	/*
1214	**  Fail if the user has a quota specified, and delivery of this
1215	**  message would exceed that quota.  We bounce such failures using
1216	**  EX_UNAVAILABLE, unless there were internal problems, since
1217	**  storing immense messages for later retries can cause queueing
1218	**  issues.
1219	*/
1220
1221	if (ui.quota > 0)
1222	{
1223		struct stat dsb;
1224
1225		if (fstat(fd, &dsb) < 0)
1226		{
1227			ExitVal = EX_TEMPFAIL;
1228			mailerr("451 4.3.0",
1229				"%s: fstat: can't stat temporary storage: %s",
1230				ui.mailspool, sm_errstring(errno));
1231			goto err1;
1232		}
1233
1234		if (dsb.st_size + sb.st_size + 1 > ui.quota)
1235		{
1236			ExitVal = EX_UNAVAILABLE;
1237			mailerr("551 5.2.2",
1238				"%s: Mailbox full or quota exceeded",
1239				ui.mailspool);
1240			goto err1;
1241		}
1242	}
1243#endif /* 0 */
1244
1245	/* Wait until we can get a lock on the file. */
1246	if (flock(mbfd, LOCK_EX) < 0)
1247	{
1248		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
1249		goto err1;
1250	}
1251
1252	/* Get the starting offset of the new message */
1253	curoff = lseek(mbfd, (off_t) 0, SEEK_END);
1254
1255	if (!nobiff)
1256	{
1257		(void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n",
1258				   name, (LONGLONG_T) curoff);
1259	}
1260
1261	/* Copy the message into the file. */
1262	if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1)
1263	{
1264		mailerr("450 4.2.0", "Temporary file: %s",
1265			sm_errstring(errno));
1266		goto err1;
1267	}
1268#ifdef DEBUG
1269	fprintf(stderr, "before writing: euid = %d\n", (int) geteuid());
1270#endif /* DEBUG */
1271#ifdef CONTENTLENGTH
1272	headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ;
1273	for (;;)
1274	{
1275		if (headerbytes == 0)
1276		{
1277			(void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr);
1278			nr = strlen(buf);
1279			headerbytes = -1;
1280			readamount = 0;
1281		}
1282		else if (headerbytes > sizeof(buf) || headerbytes < 0)
1283			readamount = sizeof(buf);
1284		else
1285			readamount = headerbytes;
1286		if (readamount != 0)
1287			nr = read(fd, buf, readamount);
1288		if (nr <= 0)
1289			break;
1290		if (headerbytes > 0)
1291			headerbytes -= nr ;
1292
1293#else /* CONTENTLENGTH */
1294	while ((nr = read(fd, buf, sizeof(buf))) > 0)
1295	{
1296#endif /* CONTENTLENGTH */
1297		for (off = 0; off < nr; off += nw)
1298		{
1299			if ((nw = write(mbfd, buf + off, nr - off)) < 0)
1300			{
1301				errcode = "450 4.2.0";
1302#ifdef EDQUOT
1303				if (errno == EDQUOT && BounceQuota)
1304					errcode = "552 5.2.2";
1305#endif /* EDQUOT */
1306				mailerr(errcode, "%s: %s",
1307					path, sm_errstring(errno));
1308				goto err3;
1309			}
1310		}
1311	}
1312	if (nr < 0)
1313	{
1314		mailerr("450 4.2.0", "Temporary file: %s",
1315			sm_errstring(errno));
1316		goto err3;
1317	}
1318
1319	/* Flush to disk, don't wait for update. */
1320	if (!nofsync && fsync(mbfd) < 0)
1321	{
1322		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
1323err3:
1324#ifdef DEBUG
1325		fprintf(stderr, "reset euid = %d\n", (int) geteuid());
1326#endif /* DEBUG */
1327		if (mbfd >= 0)
1328			(void) ftruncate(mbfd, curoff);
1329err1:		if (mbfd >= 0)
1330			(void) close(mbfd);
1331err0:		(void) setreuid(0, 0);
1332		unlockmbox();
1333		return;
1334	}
1335
1336	/*
1337	**  Save the current size so if the close() fails below
1338	**  we can make sure no other process has changed the mailbox
1339	**  between the failed close and the re-open()/re-lock().
1340	**  If something else has changed the size, we shouldn't
1341	**  try to truncate it as we may do more harm then good
1342	**  (e.g., truncate a later message delivery).
1343	*/
1344
1345	if (fstat(mbfd, &sb) < 0)
1346		cursize = 0;
1347	else
1348		cursize = sb.st_size;
1349
1350
1351	/* Close and check -- NFS doesn't write until the close. */
1352	if (close(mbfd))
1353	{
1354		errcode = "450 4.2.0";
1355#ifdef EDQUOT
1356		if (errno == EDQUOT && BounceQuota)
1357			errcode = "552 5.2.2";
1358#endif /* EDQUOT */
1359		mailerr(errcode, "%s: %s", path, sm_errstring(errno));
1360		mbfd = open(path, O_WRONLY, 0);
1361		if (mbfd < 0 ||
1362		    cursize == 0
1363		    || flock(mbfd, LOCK_EX) < 0 ||
1364		    fstat(mbfd, &sb) < 0 ||
1365		    sb.st_size != cursize ||
1366		    sb.st_nlink != 1 ||
1367		    !S_ISREG(sb.st_mode) ||
1368		    sb.st_dev != fsb.st_dev ||
1369		    sb.st_ino != fsb.st_ino ||
1370# if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
1371		    sb.st_gen != fsb.st_gen ||
1372# endif /* HAS_ST_GEN && 0 */
1373		    sb.st_uid != fsb.st_uid
1374		   )
1375		{
1376			/* Don't use a bogus file */
1377			if (mbfd >= 0)
1378			{
1379				(void) close(mbfd);
1380				mbfd = -1;
1381			}
1382		}
1383
1384		/* Attempt to truncate back to pre-write size */
1385		goto err3;
1386	}
1387	else if (!nobiff)
1388		notifybiff(biffmsg);
1389
1390	if (setreuid(0, 0) < 0)
1391	{
1392		mailerr("450 4.2.0", "setreuid(0, 0): %s",
1393			sm_errstring(errno));
1394		goto err0;
1395	}
1396#ifdef DEBUG
1397	fprintf(stderr, "reset euid = %d\n", (int) geteuid());
1398#endif /* DEBUG */
1399	unlockmbox();
1400	if (LMTPMode)
1401		printf("250 2.1.5 %s Ok\r\n", name);
1402}
1403
1404/*
1405**  user.lock files are necessary for compatibility with other
1406**  systems, e.g., when the mail spool file is NFS exported.
1407**  Alas, mailbox locking is more than just a local matter.
1408**  EPA 11/94.
1409*/
1410
1411bool	Locked = false;
1412
1413#ifdef MAILLOCK
1414int
1415lockmbox(name)
1416	char *name;
1417{
1418	int r = 0;
1419
1420	if (Locked)
1421		return 0;
1422	if ((r = maillock(name, 15)) == L_SUCCESS)
1423	{
1424		Locked = true;
1425		return 0;
1426	}
1427	switch (r)
1428	{
1429	  case L_TMPLOCK:	/* Can't create tmp file */
1430	  case L_TMPWRITE:	/* Can't write pid into lockfile */
1431	  case L_MAXTRYS:	/* Failed after retrycnt attempts */
1432		errno = 0;
1433		r = EX_TEMPFAIL;
1434		break;
1435	  case L_ERROR:		/* Check errno for reason */
1436		r = errno;
1437		break;
1438	  default:		/* other permanent errors */
1439		errno = 0;
1440		r = EX_UNAVAILABLE;
1441		break;
1442	}
1443	return r;
1444}
1445
1446void
1447unlockmbox()
1448{
1449	if (Locked)
1450		mailunlock();
1451	Locked = false;
1452}
1453#else /* MAILLOCK */
1454
1455char	LockName[MAXPATHLEN];
1456
1457int
1458lockmbox(path)
1459	char *path;
1460{
1461	int statfailed = 0;
1462	time_t start;
1463
1464	if (Locked)
1465		return 0;
1466	if (strlen(path) + 6 > sizeof LockName)
1467		return EX_SOFTWARE;
1468	(void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path);
1469	(void) time(&start);
1470	for (; ; sleep(5))
1471	{
1472		int fd;
1473		struct stat st;
1474		time_t now;
1475
1476		/* global timeout */
1477		(void) time(&now);
1478		if (now > start + LOCKTO_GLOB)
1479		{
1480			errno = 0;
1481			return EX_TEMPFAIL;
1482		}
1483		fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, LOCKFILE_PMODE);
1484		if (fd >= 0)
1485		{
1486			/* defeat lock checking programs which test pid */
1487			(void) write(fd, "0", 2);
1488			Locked = true;
1489			(void) close(fd);
1490			return 0;
1491		}
1492		if (stat(LockName, &st) < 0)
1493		{
1494			if (statfailed++ > 5)
1495			{
1496				errno = 0;
1497				return EX_TEMPFAIL;
1498			}
1499			continue;
1500		}
1501		statfailed = 0;
1502		(void) time(&now);
1503		if (now < st.st_ctime + LOCKTO_RM)
1504			continue;
1505
1506		/* try to remove stale lockfile */
1507		if (unlink(LockName) < 0)
1508			return errno;
1509	}
1510}
1511
1512void
1513unlockmbox()
1514{
1515	if (!Locked)
1516		return;
1517	(void) unlink(LockName);
1518	Locked = false;
1519}
1520#endif /* MAILLOCK */
1521
1522void
1523notifybiff(msg)
1524	char *msg;
1525{
1526	static bool initialized = false;
1527	static int f = -1;
1528	struct hostent *hp;
1529	struct servent *sp;
1530	int len;
1531	static struct sockaddr_in addr;
1532
1533	if (!initialized)
1534	{
1535		initialized = true;
1536
1537		/* Be silent if biff service not available. */
1538		if ((sp = getservbyname("biff", "udp")) == NULL ||
1539		    (hp = gethostbyname("localhost")) == NULL ||
1540		    hp->h_length != INADDRSZ)
1541			return;
1542
1543		addr.sin_family = hp->h_addrtype;
1544		memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ);
1545		addr.sin_port = sp->s_port;
1546	}
1547
1548	/* No message, just return */
1549	if (msg == NULL)
1550		return;
1551
1552	/* Couldn't initialize addr struct */
1553	if (addr.sin_family == AF_UNSPEC)
1554		return;
1555
1556	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1557		return;
1558	len = strlen(msg) + 1;
1559	(void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr));
1560}
1561
1562void
1563usage()
1564{
1565	ExitVal = EX_USAGE;
1566	mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ...");
1567	exit(ExitVal);
1568}
1569
1570void
1571/*VARARGS2*/
1572#ifdef __STDC__
1573mailerr(const char *hdr, const char *fmt, ...)
1574#else /* __STDC__ */
1575mailerr(hdr, fmt, va_alist)
1576	const char *hdr;
1577	const char *fmt;
1578	va_dcl
1579#endif /* __STDC__ */
1580{
1581	size_t len = 0;
1582	SM_VA_LOCAL_DECL
1583
1584	(void) e_to_sys(errno);
1585
1586	SM_VA_START(ap, fmt);
1587
1588	if (LMTPMode && hdr != NULL)
1589	{
1590		sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr);
1591		len = strlen(ErrBuf);
1592	}
1593	(void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap);
1594	SM_VA_END(ap);
1595
1596	if (!HoldErrs)
1597		flush_error();
1598
1599	/* Log the message to syslog. */
1600	if (!LMTPMode)
1601		syslog(LOG_ERR, "%s", ErrBuf);
1602}
1603
1604void
1605flush_error()
1606{
1607	if (LMTPMode)
1608		printf("%s\r\n", ErrBuf);
1609	else
1610	{
1611		if (ExitVal != EX_USAGE)
1612			(void) fprintf(stderr, "mail.local: ");
1613		fprintf(stderr, "%s\n", ErrBuf);
1614	}
1615}
1616
1617#if HASHSPOOL
1618const char *
1619hashname(name)
1620	char *name;
1621{
1622	static char p[MAXPATHLEN];
1623	int i;
1624	int len;
1625	char *str;
1626# if HASHSPOOLMD5
1627	char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_";
1628	MD5_CTX ctx;
1629	unsigned char md5[18];
1630#  if MAXPATHLEN <= 24
1631    ERROR _MAXPATHLEN <= 24
1632#  endif /* MAXPATHLEN <= 24 */
1633	char b64[24];
1634	MD5_LONG bits;
1635	int j;
1636# endif /* HASHSPOOLMD5 */
1637
1638	if (HashType == HASH_NONE || HashDepth * 2 >= MAXPATHLEN)
1639	{
1640		p[0] = '\0';
1641		return p;
1642	}
1643
1644	switch(HashType)
1645	{
1646	  case HASH_USER:
1647		str = name;
1648		break;
1649
1650# if HASHSPOOLMD5
1651	  case HASH_MD5:
1652		MD5_Init(&ctx);
1653		MD5_Update(&ctx, name, strlen(name));
1654		MD5_Final(md5, &ctx);
1655		md5[16] = 0;
1656		md5[17] = 0;
1657
1658		for (i = 0; i < 6; i++)
1659		{
1660			bits = (unsigned) md5[(3 * i)] << 16;
1661			bits |= (unsigned) md5[(3 * i) + 1] << 8;
1662			bits |= (unsigned) md5[(3 * i) + 2];
1663
1664			for (j = 3; j >= 0; j--)
1665			{
1666				b64[(4 * i) + j] = Base64[(bits & 0x3f)];
1667				bits >>= 6;
1668			}
1669		}
1670		b64[22] = '\0';
1671		str = b64;
1672		break;
1673# endif /* HASHSPOOLMD5 */
1674	}
1675
1676	len = strlen(str);
1677	for (i = 0; i < HashDepth; i++)
1678	{
1679		if (i < len)
1680			p[i * 2] = str[i];
1681		else
1682			p[i * 2] = '_';
1683		p[(i * 2) + 1] = '/';
1684	}
1685	p[HashDepth * 2] = '\0';
1686	return p;
1687}
1688#endif /* HASHSPOOL */
1689
1690/*
1691 * e_to_sys --
1692 *	Guess which errno's are temporary.  Gag me.
1693 */
1694
1695int
1696e_to_sys(num)
1697	int num;
1698{
1699	/* Temporary failures override hard errors. */
1700	if (ExitVal == EX_TEMPFAIL)
1701		return ExitVal;
1702
1703	switch (num)		/* Hopefully temporary errors. */
1704	{
1705#ifdef EDQUOT
1706	  case EDQUOT:		/* Disc quota exceeded */
1707		if (BounceQuota)
1708		{
1709			ExitVal = EX_UNAVAILABLE;
1710			break;
1711		}
1712		/* FALLTHROUGH */
1713#endif /* EDQUOT */
1714#ifdef EAGAIN
1715	  case EAGAIN:		/* Resource temporarily unavailable */
1716#endif /* EAGAIN */
1717#ifdef EBUSY
1718	  case EBUSY:		/* Device busy */
1719#endif /* EBUSY */
1720#ifdef EPROCLIM
1721	  case EPROCLIM:	/* Too many processes */
1722#endif /* EPROCLIM */
1723#ifdef EUSERS
1724	  case EUSERS:		/* Too many users */
1725#endif /* EUSERS */
1726#ifdef ECONNABORTED
1727	  case ECONNABORTED:	/* Software caused connection abort */
1728#endif /* ECONNABORTED */
1729#ifdef ECONNREFUSED
1730	  case ECONNREFUSED:	/* Connection refused */
1731#endif /* ECONNREFUSED */
1732#ifdef ECONNRESET
1733	  case ECONNRESET:	/* Connection reset by peer */
1734#endif /* ECONNRESET */
1735#ifdef EDEADLK
1736	  case EDEADLK:		/* Resource deadlock avoided */
1737#endif /* EDEADLK */
1738#ifdef EFBIG
1739	  case EFBIG:		/* File too large */
1740#endif /* EFBIG */
1741#ifdef EHOSTDOWN
1742	  case EHOSTDOWN:	/* Host is down */
1743#endif /* EHOSTDOWN */
1744#ifdef EHOSTUNREACH
1745	  case EHOSTUNREACH:	/* No route to host */
1746#endif /* EHOSTUNREACH */
1747#ifdef EMFILE
1748	  case EMFILE:		/* Too many open files */
1749#endif /* EMFILE */
1750#ifdef ENETDOWN
1751	  case ENETDOWN:	/* Network is down */
1752#endif /* ENETDOWN */
1753#ifdef ENETRESET
1754	  case ENETRESET:	/* Network dropped connection on reset */
1755#endif /* ENETRESET */
1756#ifdef ENETUNREACH
1757	  case ENETUNREACH:	/* Network is unreachable */
1758#endif /* ENETUNREACH */
1759#ifdef ENFILE
1760	  case ENFILE:		/* Too many open files in system */
1761#endif /* ENFILE */
1762#ifdef ENOBUFS
1763	  case ENOBUFS:		/* No buffer space available */
1764#endif /* ENOBUFS */
1765#ifdef ENOMEM
1766	  case ENOMEM:		/* Cannot allocate memory */
1767#endif /* ENOMEM */
1768#ifdef ENOSPC
1769	  case ENOSPC:		/* No space left on device */
1770#endif /* ENOSPC */
1771#ifdef EROFS
1772	  case EROFS:		/* Read-only file system */
1773#endif /* EROFS */
1774#ifdef ESTALE
1775	  case ESTALE:		/* Stale NFS file handle */
1776#endif /* ESTALE */
1777#ifdef ETIMEDOUT
1778	  case ETIMEDOUT:	/* Connection timed out */
1779#endif /* ETIMEDOUT */
1780#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
1781	  case EWOULDBLOCK:	/* Operation would block. */
1782#endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */
1783		ExitVal = EX_TEMPFAIL;
1784		break;
1785
1786	  default:
1787		ExitVal = EX_UNAVAILABLE;
1788		break;
1789	}
1790	return ExitVal;
1791}
1792
1793#if defined(ultrix) || defined(_CRAY)
1794/*
1795 * Copyright (c) 1987, 1993
1796 *	The Regents of the University of California.  All rights reserved.
1797 *
1798 * Redistribution and use in source and binary forms, with or without
1799 * modification, are permitted provided that the following conditions
1800 * are met:
1801 * 1. Redistributions of source code must retain the above copyright
1802 *    notice, this list of conditions and the following disclaimer.
1803 * 2. Redistributions in binary form must reproduce the above copyright
1804 *    notice, this list of conditions and the following disclaimer in the
1805 *    documentation and/or other materials provided with the distribution.
1806 * 3. All advertising materials mentioning features or use of this software
1807 *    must display the following acknowledgement:
1808 *	This product includes software developed by the University of
1809 *	California, Berkeley and its contributors.
1810 * 4. Neither the name of the University nor the names of its contributors
1811 *    may be used to endorse or promote products derived from this software
1812 *    without specific prior written permission.
1813 *
1814 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1815 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1816 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1817 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1818 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1819 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1820 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1821 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1822 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1823 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1824 * SUCH DAMAGE.
1825 */
1826
1827# if defined(LIBC_SCCS) && !defined(lint)
1828static char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
1829# endif /* defined(LIBC_SCCS) && !defined(lint) */
1830
1831# include <sys/types.h>
1832# include <sys/stat.h>
1833# include <fcntl.h>
1834# include <errno.h>
1835# include <stdio.h>
1836# include <ctype.h>
1837
1838static int _gettemp();
1839
1840mkstemp(path)
1841	char *path;
1842{
1843	int fd;
1844
1845	return (_gettemp(path, &fd) ? fd : -1);
1846}
1847
1848static
1849_gettemp(path, doopen)
1850	char *path;
1851	register int *doopen;
1852{
1853	extern int errno;
1854	register char *start, *trv;
1855	struct stat sbuf;
1856	unsigned int pid;
1857
1858	pid = getpid();
1859	for (trv = path; *trv; ++trv);		/* extra X's get set to 0's */
1860	while (*--trv == 'X')
1861	{
1862		*trv = (pid % 10) + '0';
1863		pid /= 10;
1864	}
1865
1866	/*
1867	 * check the target directory; if you have six X's and it
1868	 * doesn't exist this runs for a *very* long time.
1869	 */
1870	for (start = trv + 1;; --trv)
1871	{
1872		if (trv <= path)
1873			break;
1874		if (*trv == '/')
1875		{
1876			*trv = '\0';
1877			if (stat(path, &sbuf) < 0)
1878				return(0);
1879			if (!S_ISDIR(sbuf.st_mode))
1880			{
1881				errno = ENOTDIR;
1882				return(0);
1883			}
1884			*trv = '/';
1885			break;
1886		}
1887	}
1888
1889	for (;;)
1890	{
1891		if (doopen)
1892		{
1893			if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR,
1894					    0600)) >= 0)
1895				return(1);
1896			if (errno != EEXIST)
1897				return(0);
1898		}
1899		else if (stat(path, &sbuf) < 0)
1900			return(errno == ENOENT ? 1 : 0);
1901
1902		/* tricky little algorithm for backward compatibility */
1903		for (trv = start;;)
1904		{
1905			if (!*trv)
1906				return(0);
1907			if (*trv == 'z')
1908				*trv++ = 'a';
1909			else
1910			{
1911				if (isascii(*trv) && isdigit(*trv))
1912					*trv = 'a';
1913				else
1914					++*trv;
1915				break;
1916			}
1917		}
1918	}
1919	/* NOTREACHED */
1920}
1921#endif /* defined(ultrix) || defined(_CRAY) */
1922