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*46034Sbostic static char sccsid[] = "@(#)mail.local.c 5.1 (Berkeley) 01/19/91"; 1645976Sbostic #endif /* not lint */ 1745976Sbostic 1829763Skarels #include <sys/param.h> 1916731Sralph #include <sys/stat.h> 2016731Sralph #include <sys/file.h> 2145976Sbostic #include <sys/socket.h> 2245976Sbostic #include <sys/syslog.h> 2345976Sbostic #include <sys/errno.h> 2445976Sbostic #include <netinet/in.h> 2545976Sbostic #include <netdb.h> 2645976Sbostic #include <pwd.h> 2745976Sbostic #include <time.h> 2845976Sbostic #include <varargs.h> 29579Sroot #include <stdio.h> 3045976Sbostic #include <string.h> 3145976Sbostic #include <stdlib.h> 3237465Sbostic #include "pathnames.h" 33579Sroot 3445976Sbostic #define FATAL 1 3545976Sbostic #define NOTFATAL 0 36579Sroot 37579Sroot main(argc, argv) 3845976Sbostic int argc; 3916731Sralph char **argv; 40579Sroot { 4145976Sbostic extern int optind; 4245976Sbostic extern char *optarg; 4345976Sbostic struct passwd *pw; 4445976Sbostic int ch, fd, eval; 4545976Sbostic uid_t uid; 4645976Sbostic char *from; 47579Sroot 4845976Sbostic openlog("mail.local", LOG_PERROR, LOG_MAIL); 4916731Sralph 5045976Sbostic from = NULL; 5145976Sbostic while ((ch = getopt(argc, argv, "df:r:")) != EOF) 5245976Sbostic switch(ch) { 5345976Sbostic case 'd': /* backward compatible */ 5416731Sralph break; 5516731Sralph case 'f': 5645976Sbostic case 'r': /* backward compatible */ 5745976Sbostic if (from) 5845976Sbostic error(FATAL, "multiple -f options."); 5945976Sbostic from = optarg; 60579Sroot break; 6145976Sbostic case '?': 6216731Sralph default: 6345976Sbostic usage(); 6416731Sralph } 6545976Sbostic argc -= optind; 6645976Sbostic argv += optind; 67579Sroot 6845976Sbostic if (!*argv) 6945976Sbostic usage(); 70579Sroot 7145976Sbostic /* 7245976Sbostic * If from not specified, use the name from getlogin() if the 7345976Sbostic * uid matches, otherwise, use the name from the password file 7445976Sbostic * corresponding to the uid. 7545976Sbostic */ 7645976Sbostic uid = getuid(); 77*46034Sbostic if (!from && (!(from = getlogin()) || 78*46034Sbostic !(pw = getpwnam(from)) || pw->pw_uid != uid)) 7945976Sbostic from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; 80579Sroot 8145976Sbostic fd = store(from); 8245976Sbostic for (eval = 0; *argv; ++argv) 8345976Sbostic eval |= deliver(fd, *argv); 8445976Sbostic exit(eval); 85579Sroot } 86579Sroot 8745976Sbostic store(from) 8845976Sbostic char *from; 89579Sroot { 9045976Sbostic FILE *fp; 9145976Sbostic time_t tval; 9245976Sbostic int fd, eline; 9345976Sbostic char *tn, line[2048]; 94579Sroot 9545976Sbostic tn = _PATH_LOCTMP; 9645976Sbostic if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+"))) 9745976Sbostic error(FATAL, "unable to open temporary file."); 9845976Sbostic (void)unlink(tn); 99579Sroot 10045976Sbostic (void)time(&tval); 10145976Sbostic (void)fprintf(fp, "From %s %s", from, ctime(&tval)); 102579Sroot 10345976Sbostic line[0] = '\0'; 10445976Sbostic for (eline = 1; fgets(line, sizeof(line), stdin);) { 10545976Sbostic if (line[0] == '\n') 10645976Sbostic eline = 1; 10745976Sbostic else { 10845976Sbostic if (eline && line[0] == 'F' && !bcmp(line, "From ", 5)) 10945976Sbostic (void)putc('>', fp); 11045976Sbostic eline = 0; 11145976Sbostic } 11245976Sbostic (void)fprintf(fp, "%s", line); 11345976Sbostic if (ferror(fp)) 11445976Sbostic break; 115579Sroot } 116579Sroot 11745976Sbostic /* If message not newline terminated, need an extra. */ 11845976Sbostic if (!index(line, '\n')) 11945976Sbostic (void)putc('\n', fp); 12045976Sbostic /* Output a newline; note, empty messages are allowed. */ 12145976Sbostic (void)putc('\n', fp); 12211893Seric 12345976Sbostic (void)fflush(fp); 12445976Sbostic if (ferror(fp)) 12545976Sbostic error(FATAL, "temporary file write error."); 12645976Sbostic return(fd); 127579Sroot } 128579Sroot 12945976Sbostic deliver(fd, name) 13045976Sbostic int fd; 13145976Sbostic char *name; 132579Sroot { 13345976Sbostic struct stat sb; 13445976Sbostic struct passwd *pw; 13545976Sbostic int created, mbfd, nr, nw, off, rval; 13645976Sbostic char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; 13745976Sbostic off_t curoff, lseek(); 138579Sroot 13945976Sbostic /* 14045976Sbostic * Disallow delivery to unknown names -- special mailboxes can be 14145976Sbostic * handled in the sendmail aliases file. 14245976Sbostic */ 14345976Sbostic if (!(pw = getpwnam(name))) { 14445976Sbostic error(NOTFATAL, "unknown name: %s.", name); 14545976Sbostic return(1); 14645976Sbostic } 147579Sroot 14845976Sbostic (void)sprintf(path, "%s/%s", _PATH_MAILDIR, name); 149579Sroot 15045976Sbostic if (!(created = lstat(path, &sb)) && 15145976Sbostic (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) { 15245976Sbostic error(NOTFATAL, "%s: linked file.", path); 15345976Sbostic return(1); 15416731Sralph } 155579Sroot 156579Sroot /* 15745976Sbostic * There's a race here -- two processes think they both created 15845976Sbostic * the file. This means the file cannot be unlinked. 159579Sroot */ 16045976Sbostic if ((mbfd = 16145976Sbostic open(path, O_APPEND|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) { 16245976Sbostic error(NOTFATAL, "%s: %s.", path, strerror(errno)); 16345976Sbostic return(1); 16445976Sbostic } 165579Sroot 16645976Sbostic rval = 0; 16745976Sbostic /* XXX: Open should allow flock'ing the file; see 4.4BSD. */ 16845976Sbostic if (flock(mbfd, LOCK_EX)) { 16945976Sbostic error(NOTFATAL, "%s: %s.", path, strerror(errno)); 17045976Sbostic rval = 1; 17145976Sbostic goto bad; 17245976Sbostic } 173579Sroot 17445976Sbostic curoff = lseek(mbfd, 0L, SEEK_END); 17545976Sbostic (void)sprintf(biffmsg, "%s@%ld\n", name, curoff); 17645976Sbostic if (lseek(fd, 0L, SEEK_SET) == (off_t)-1) { 17745976Sbostic error(FATAL, "temporary file: %s.", strerror(errno)); 17845976Sbostic rval = 1; 17945976Sbostic goto bad; 18045976Sbostic } 181579Sroot 18245976Sbostic while ((nr = read(fd, buf, sizeof(buf))) > 0) 18345976Sbostic for (off = 0; off < nr; nr -= nw, off += nw) 18445976Sbostic if ((nw = write(mbfd, buf + off, nr)) < 0) { 18545976Sbostic error(NOTFATAL, 18645976Sbostic "%s: %s.", path, strerror(errno)); 18745976Sbostic goto trunc; 18845976Sbostic } 18945976Sbostic if (nr < 0) { 19045976Sbostic error(FATAL, "temporary file: %s.", strerror(errno)); 19145976Sbostic trunc: (void)ftruncate(mbfd, curoff); 19245976Sbostic rval = 1; 193579Sroot } 194579Sroot 19545976Sbostic /* 19645976Sbostic * Set the owner and group. Historically, binmail repeated this at 19745976Sbostic * each mail delivery. We no longer do this, assuming that if the 19845976Sbostic * ownership or permissions were changed there was a reason for doing 19945976Sbostic * so. 20045976Sbostic */ 20145976Sbostic bad: if (created) 20245976Sbostic (void)fchown(mbfd, pw->pw_uid, pw->pw_gid); 203579Sroot 20445976Sbostic /* Implicit unlock. */ 20545976Sbostic (void)close(mbfd); 206579Sroot 20745976Sbostic if (!rval) 20845976Sbostic notifybiff(biffmsg); 20945976Sbostic return(rval); 210579Sroot } 211579Sroot 21216731Sralph notifybiff(msg) 21316731Sralph char *msg; 21416731Sralph { 21516731Sralph static struct sockaddr_in addr; 21616731Sralph static int f = -1; 21745976Sbostic struct hostent *hp; 21845976Sbostic struct servent *sp; 21945976Sbostic int len; 22016731Sralph 22145976Sbostic if (!addr.sin_family) { 22245976Sbostic /* Be silent if biff service not available. */ 22345976Sbostic if (!(sp = getservbyname("biff", "udp"))) 22445976Sbostic return; 22545976Sbostic if (!(hp = gethostbyname("localhost"))) { 22645976Sbostic error(NOTFATAL, "localhost: %s.", strerror(errno)); 22745976Sbostic return; 22816731Sralph } 22945976Sbostic addr.sin_family = hp->h_addrtype; 23045976Sbostic bcopy(hp->h_addr, &addr.sin_addr, hp->h_length); 23145976Sbostic addr.sin_port = sp->s_port; 23216731Sralph } 23345976Sbostic if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 23445976Sbostic error(NOTFATAL, "socket: %s.", strerror(errno)); 23545976Sbostic return; 23616731Sralph } 23745976Sbostic len = strlen(msg) + 1; 23845976Sbostic if (sendto(f, msg, len, 0, &addr, sizeof(addr)) != len) 23945976Sbostic error(NOTFATAL, "sendto biff: %s.", strerror(errno)); 24016731Sralph } 24116731Sralph 24245976Sbostic usage() 243579Sroot { 24445976Sbostic error(FATAL, "usage: mail.local [-f from] user ..."); 245579Sroot } 246579Sroot 24745976Sbostic /* VARARGS */ 24845976Sbostic error(va_alist) 24945976Sbostic va_dcl 250579Sroot { 25145976Sbostic va_list ap; 25245976Sbostic int isfatal; 25345976Sbostic char *fmt; 254579Sroot 25545976Sbostic va_start(ap); 25645976Sbostic isfatal = va_arg(ap, int); 25745976Sbostic fmt = va_arg(ap, char *); 25845976Sbostic vsyslog(LOG_ERR, fmt, ap); 25945976Sbostic va_end(ap); 26045976Sbostic if (isfatal) 26145976Sbostic exit(1); 262579Sroot } 263