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