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