145976Sbostic /*-
245976Sbostic  * Copyright (c) 1990 The Regents of the University of California.
345976Sbostic  * All rights reserved.
445976Sbostic  *
545976Sbostic  * %sccs.include.redist.c%
645976Sbostic  */
745976Sbostic 
814465Ssam #ifndef lint
945976Sbostic char copyright[] =
1045976Sbostic "@(#) Copyright (c) 1990 The Regents of the University of California.\n\
1145976Sbostic  All rights reserved.\n";
1245976Sbostic #endif /* not lint */
1314465Ssam 
1445976Sbostic #ifndef lint
15*58400Sbostic static char sccsid[] = "@(#)mail.local.c	5.11 (Berkeley) 03/02/93";
1645976Sbostic #endif /* not lint */
1745976Sbostic 
1829763Skarels #include <sys/param.h>
1916731Sralph #include <sys/stat.h>
2045976Sbostic #include <sys/socket.h>
2157648Sbostic 
2245976Sbostic #include <netinet/in.h>
2357648Sbostic 
2457648Sbostic #include <errno.h>
2546672Sbostic #include <fcntl.h>
2645976Sbostic #include <netdb.h>
2745976Sbostic #include <pwd.h>
28579Sroot #include <stdio.h>
2946672Sbostic #include <stdlib.h>
3045976Sbostic #include <string.h>
3157648Sbostic #include <syslog.h>
3257648Sbostic #include <time.h>
3357648Sbostic #include <unistd.h>
3457648Sbostic 
3537465Sbostic #include "pathnames.h"
36579Sroot 
3745976Sbostic #define	FATAL		1
3845976Sbostic #define	NOTFATAL	0
39579Sroot 
4050092Sbostic int	deliver __P((int, char *));
4150092Sbostic void	err __P((int, const char *, ...));
4250092Sbostic void	notifybiff __P((char *));
4350092Sbostic int	store __P((char *));
4450092Sbostic void	usage __P((void));
4550092Sbostic 
4657648Sbostic int
47579Sroot main(argc, argv)
4845976Sbostic 	int argc;
4916731Sralph 	char **argv;
50579Sroot {
5145976Sbostic 	struct passwd *pw;
5245976Sbostic 	int ch, fd, eval;
5345976Sbostic 	uid_t uid;
5445976Sbostic 	char *from;
55579Sroot 
56*58400Sbostic 	openlog("mail.local", 0, LOG_MAIL);
5716731Sralph 
5845976Sbostic 	from = NULL;
5945976Sbostic 	while ((ch = getopt(argc, argv, "df:r:")) != EOF)
6045976Sbostic 		switch(ch) {
6145976Sbostic 		case 'd':		/* backward compatible */
6216731Sralph 			break;
6316731Sralph 		case 'f':
6445976Sbostic 		case 'r':		/* backward compatible */
6545976Sbostic 			if (from)
6650092Sbostic 			    err(FATAL, "multiple -f options");
6745976Sbostic 			from = optarg;
68579Sroot 			break;
6945976Sbostic 		case '?':
7016731Sralph 		default:
7145976Sbostic 			usage();
7216731Sralph 		}
7345976Sbostic 	argc -= optind;
7445976Sbostic 	argv += optind;
75579Sroot 
7645976Sbostic 	if (!*argv)
7745976Sbostic 		usage();
78579Sroot 
7945976Sbostic 	/*
8045976Sbostic 	 * If from not specified, use the name from getlogin() if the
8145976Sbostic 	 * uid matches, otherwise, use the name from the password file
8245976Sbostic 	 * corresponding to the uid.
8345976Sbostic 	 */
8445976Sbostic 	uid = getuid();
8546034Sbostic 	if (!from && (!(from = getlogin()) ||
8646034Sbostic 	    !(pw = getpwnam(from)) || pw->pw_uid != uid))
8745976Sbostic 		from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
88579Sroot 
8945976Sbostic 	fd = store(from);
9045976Sbostic 	for (eval = 0; *argv; ++argv)
9145976Sbostic 		eval |= deliver(fd, *argv);
9245976Sbostic 	exit(eval);
93579Sroot }
94579Sroot 
9557648Sbostic int
9645976Sbostic store(from)
9745976Sbostic 	char *from;
98579Sroot {
9945976Sbostic 	FILE *fp;
10045976Sbostic 	time_t tval;
10145976Sbostic 	int fd, eline;
10245976Sbostic 	char *tn, line[2048];
103579Sroot 
10447075Sdonn 	tn = strdup(_PATH_LOCTMP);
10557648Sbostic 	if ((fd = mkstemp(tn)) == -1 || (fp = fdopen(fd, "w+")) == NULL)
10650092Sbostic 		err(FATAL, "unable to open temporary file");
10745976Sbostic 	(void)unlink(tn);
10847075Sdonn 	free(tn);
109579Sroot 
11045976Sbostic 	(void)time(&tval);
11145976Sbostic 	(void)fprintf(fp, "From %s %s", from, ctime(&tval));
112579Sroot 
11345976Sbostic 	line[0] = '\0';
11445976Sbostic 	for (eline = 1; fgets(line, sizeof(line), stdin);) {
11545976Sbostic 		if (line[0] == '\n')
11645976Sbostic 			eline = 1;
11745976Sbostic 		else {
11845976Sbostic 			if (eline && line[0] == 'F' && !bcmp(line, "From ", 5))
11945976Sbostic 				(void)putc('>', fp);
12045976Sbostic 			eline = 0;
12145976Sbostic 		}
12245976Sbostic 		(void)fprintf(fp, "%s", line);
12345976Sbostic 		if (ferror(fp))
12445976Sbostic 			break;
125579Sroot 	}
126579Sroot 
12745976Sbostic 	/* If message not newline terminated, need an extra. */
12845976Sbostic 	if (!index(line, '\n'))
12945976Sbostic 		(void)putc('\n', fp);
13045976Sbostic 	/* Output a newline; note, empty messages are allowed. */
13145976Sbostic 	(void)putc('\n', fp);
13211893Seric 
13345976Sbostic 	(void)fflush(fp);
13445976Sbostic 	if (ferror(fp))
13550092Sbostic 		err(FATAL, "temporary file write error");
13645976Sbostic 	return(fd);
137579Sroot }
138579Sroot 
13957648Sbostic int
14045976Sbostic deliver(fd, name)
14145976Sbostic 	int fd;
14245976Sbostic 	char *name;
143579Sroot {
14445976Sbostic 	struct stat sb;
14545976Sbostic 	struct passwd *pw;
14645976Sbostic 	int created, mbfd, nr, nw, off, rval;
14745976Sbostic 	char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
14854203Sbostic 	off_t curoff;
149579Sroot 
15045976Sbostic 	/*
15145976Sbostic 	 * Disallow delivery to unknown names -- special mailboxes can be
15245976Sbostic 	 * handled in the sendmail aliases file.
15345976Sbostic 	 */
15445976Sbostic 	if (!(pw = getpwnam(name))) {
15550092Sbostic 		err(NOTFATAL, "unknown name: %s", name);
15645976Sbostic 		return(1);
15745976Sbostic 	}
158579Sroot 
15957648Sbostic 	(void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
160579Sroot 
16145976Sbostic 	if (!(created = lstat(path, &sb)) &&
16245976Sbostic 	    (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) {
16350092Sbostic 		err(NOTFATAL, "%s: linked file", path);
16445976Sbostic 		return(1);
16516731Sralph 	}
166579Sroot 
167579Sroot 	/*
16845976Sbostic 	 * There's a race here -- two processes think they both created
16945976Sbostic 	 * the file.  This means the file cannot be unlinked.
170579Sroot 	 */
17145976Sbostic 	if ((mbfd =
17245976Sbostic 	    open(path, O_APPEND|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) {
17350092Sbostic 		err(NOTFATAL, "%s: %s", path, strerror(errno));
17445976Sbostic 		return(1);
17545976Sbostic 	}
176579Sroot 
17745976Sbostic 	rval = 0;
17845976Sbostic 	/* XXX: Open should allow flock'ing the file; see 4.4BSD. */
17945976Sbostic 	if (flock(mbfd, LOCK_EX)) {
18050092Sbostic 		err(NOTFATAL, "%s: %s", path, strerror(errno));
18145976Sbostic 		rval = 1;
18245976Sbostic 		goto bad;
18345976Sbostic 	}
184579Sroot 
18554203Sbostic 	curoff = lseek(mbfd, (off_t)0, SEEK_END);
18657648Sbostic 	(void)snprintf(biffmsg, sizeof(biffmsg), "%s@%qd\n", name, curoff);
18754203Sbostic 	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
18850092Sbostic 		err(FATAL, "temporary file: %s", strerror(errno));
18945976Sbostic 		rval = 1;
19045976Sbostic 		goto bad;
19145976Sbostic 	}
192579Sroot 
19345976Sbostic 	while ((nr = read(fd, buf, sizeof(buf))) > 0)
19445976Sbostic 		for (off = 0; off < nr; nr -= nw, off += nw)
19545976Sbostic 			if ((nw = write(mbfd, buf + off, nr)) < 0) {
19650092Sbostic 				err(NOTFATAL, "%s: %s", path, strerror(errno));
19745976Sbostic 				goto trunc;
19845976Sbostic 			}
19945976Sbostic 	if (nr < 0) {
20050092Sbostic 		err(FATAL, "temporary file: %s", strerror(errno));
20145976Sbostic trunc:		(void)ftruncate(mbfd, curoff);
20245976Sbostic 		rval = 1;
203579Sroot 	}
204579Sroot 
20545976Sbostic 	/*
20645976Sbostic 	 * Set the owner and group.  Historically, binmail repeated this at
20745976Sbostic 	 * each mail delivery.  We no longer do this, assuming that if the
20845976Sbostic 	 * ownership or permissions were changed there was a reason for doing
20945976Sbostic 	 * so.
21045976Sbostic 	 */
21145976Sbostic bad:	if (created)
21245976Sbostic 		(void)fchown(mbfd, pw->pw_uid, pw->pw_gid);
213579Sroot 
21446541Sbostic 	(void)fsync(mbfd);		/* Don't wait for update. */
21546541Sbostic 	(void)close(mbfd);		/* Implicit unlock. */
216579Sroot 
21745976Sbostic 	if (!rval)
21845976Sbostic 		notifybiff(biffmsg);
21945976Sbostic 	return(rval);
220579Sroot }
221579Sroot 
22250092Sbostic void
22316731Sralph notifybiff(msg)
22416731Sralph 	char *msg;
22516731Sralph {
22616731Sralph 	static struct sockaddr_in addr;
22716731Sralph 	static int f = -1;
22845976Sbostic 	struct hostent *hp;
22945976Sbostic 	struct servent *sp;
23045976Sbostic 	int len;
23116731Sralph 
23245976Sbostic 	if (!addr.sin_family) {
23345976Sbostic 		/* Be silent if biff service not available. */
23445976Sbostic 		if (!(sp = getservbyname("biff", "udp")))
23545976Sbostic 			return;
23645976Sbostic 		if (!(hp = gethostbyname("localhost"))) {
23750092Sbostic 			err(NOTFATAL, "localhost: %s", strerror(errno));
23845976Sbostic 			return;
23916731Sralph 		}
24045976Sbostic 		addr.sin_family = hp->h_addrtype;
24145976Sbostic 		bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
24245976Sbostic 		addr.sin_port = sp->s_port;
24316731Sralph 	}
24445976Sbostic 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
24550092Sbostic 		err(NOTFATAL, "socket: %s", strerror(errno));
24645976Sbostic 		return;
24716731Sralph 	}
24845976Sbostic 	len = strlen(msg) + 1;
24946672Sbostic 	if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
25046672Sbostic 	    != len)
25150092Sbostic 		err(NOTFATAL, "sendto biff: %s", strerror(errno));
25216731Sralph }
25316731Sralph 
25450092Sbostic void
25545976Sbostic usage()
256579Sroot {
25750092Sbostic 	err(FATAL, "usage: mail.local [-f from] user ...");
258579Sroot }
259579Sroot 
26050092Sbostic #if __STDC__
26150092Sbostic #include <stdarg.h>
26250092Sbostic #else
26350092Sbostic #include <varargs.h>
26450092Sbostic #endif
26550092Sbostic 
26650092Sbostic void
26750092Sbostic #if __STDC__
26850092Sbostic err(int isfatal, const char *fmt, ...)
26950092Sbostic #else
27051674Sbostic err(isfatal, fmt, va_alist)
27146554Sbostic 	int isfatal;
27246554Sbostic 	char *fmt;
27350092Sbostic 	va_dcl
27450092Sbostic #endif
275579Sroot {
27645976Sbostic 	va_list ap;
27750092Sbostic #if __STDC__
27846554Sbostic 	va_start(ap, fmt);
27950092Sbostic #else
28050092Sbostic 	va_start(ap);
28150092Sbostic #endif
282*58400Sbostic 	/*
283*58400Sbostic 	 * Don't use LOG_PERROR as an openlog() flag to do this, it's
284*58400Sbostic 	 * not portable enough.
285*58400Sbostic 	 */
28658396Sbostic 	(void)vfprintf(stderr, fmt, ap);
287*58400Sbostic 	(void)fprintf(stderr, "\n");
28845976Sbostic 	vsyslog(LOG_ERR, fmt, ap);
28945976Sbostic 	va_end(ap);
29045976Sbostic 	if (isfatal)
29145976Sbostic 		exit(1);
292579Sroot }
293