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