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*51674Sbostic static char sccsid[] = "@(#)mail.local.c 5.7 (Berkeley) 11/13/91"; 1645976Sbostic #endif /* not lint */ 1745976Sbostic 1829763Skarels #include <sys/param.h> 1916731Sralph #include <sys/stat.h> 2045976Sbostic #include <sys/socket.h> 2145976Sbostic #include <netinet/in.h> 2246672Sbostic #include <syslog.h> 2346672Sbostic #include <fcntl.h> 2445976Sbostic #include <netdb.h> 2545976Sbostic #include <pwd.h> 2645976Sbostic #include <time.h> 2746672Sbostic #include <unistd.h> 2850092Sbostic #include <errno.h> 29579Sroot #include <stdio.h> 3046672Sbostic #include <stdlib.h> 3145976Sbostic #include <string.h> 3237465Sbostic #include "pathnames.h" 33579Sroot 3445976Sbostic #define FATAL 1 3545976Sbostic #define NOTFATAL 0 36579Sroot 3750092Sbostic int deliver __P((int, char *)); 3850092Sbostic void err __P((int, const char *, ...)); 3950092Sbostic void notifybiff __P((char *)); 4050092Sbostic int store __P((char *)); 4150092Sbostic void usage __P((void)); 4250092Sbostic 43579Sroot main(argc, argv) 4445976Sbostic int argc; 4516731Sralph char **argv; 46579Sroot { 4745976Sbostic extern int optind; 4845976Sbostic extern char *optarg; 4945976Sbostic struct passwd *pw; 5045976Sbostic int ch, fd, eval; 5145976Sbostic uid_t uid; 5245976Sbostic char *from; 53579Sroot 5445976Sbostic openlog("mail.local", LOG_PERROR, LOG_MAIL); 5516731Sralph 5645976Sbostic from = NULL; 5745976Sbostic while ((ch = getopt(argc, argv, "df:r:")) != EOF) 5845976Sbostic switch(ch) { 5945976Sbostic case 'd': /* backward compatible */ 6016731Sralph break; 6116731Sralph case 'f': 6245976Sbostic case 'r': /* backward compatible */ 6345976Sbostic if (from) 6450092Sbostic err(FATAL, "multiple -f options"); 6545976Sbostic from = optarg; 66579Sroot break; 6745976Sbostic case '?': 6816731Sralph default: 6945976Sbostic usage(); 7016731Sralph } 7145976Sbostic argc -= optind; 7245976Sbostic argv += optind; 73579Sroot 7445976Sbostic if (!*argv) 7545976Sbostic usage(); 76579Sroot 7745976Sbostic /* 7845976Sbostic * If from not specified, use the name from getlogin() if the 7945976Sbostic * uid matches, otherwise, use the name from the password file 8045976Sbostic * corresponding to the uid. 8145976Sbostic */ 8245976Sbostic uid = getuid(); 8346034Sbostic if (!from && (!(from = getlogin()) || 8446034Sbostic !(pw = getpwnam(from)) || pw->pw_uid != uid)) 8545976Sbostic from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; 86579Sroot 8745976Sbostic fd = store(from); 8845976Sbostic for (eval = 0; *argv; ++argv) 8945976Sbostic eval |= deliver(fd, *argv); 9045976Sbostic exit(eval); 91579Sroot } 92579Sroot 9345976Sbostic store(from) 9445976Sbostic char *from; 95579Sroot { 9645976Sbostic FILE *fp; 9745976Sbostic time_t tval; 9845976Sbostic int fd, eline; 9945976Sbostic char *tn, line[2048]; 100579Sroot 10147075Sdonn tn = strdup(_PATH_LOCTMP); 10245976Sbostic if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+"))) 10350092Sbostic err(FATAL, "unable to open temporary file"); 10445976Sbostic (void)unlink(tn); 10547075Sdonn free(tn); 106579Sroot 10745976Sbostic (void)time(&tval); 10845976Sbostic (void)fprintf(fp, "From %s %s", from, ctime(&tval)); 109579Sroot 11045976Sbostic line[0] = '\0'; 11145976Sbostic for (eline = 1; fgets(line, sizeof(line), stdin);) { 11245976Sbostic if (line[0] == '\n') 11345976Sbostic eline = 1; 11445976Sbostic else { 11545976Sbostic if (eline && line[0] == 'F' && !bcmp(line, "From ", 5)) 11645976Sbostic (void)putc('>', fp); 11745976Sbostic eline = 0; 11845976Sbostic } 11945976Sbostic (void)fprintf(fp, "%s", line); 12045976Sbostic if (ferror(fp)) 12145976Sbostic break; 122579Sroot } 123579Sroot 12445976Sbostic /* If message not newline terminated, need an extra. */ 12545976Sbostic if (!index(line, '\n')) 12645976Sbostic (void)putc('\n', fp); 12745976Sbostic /* Output a newline; note, empty messages are allowed. */ 12845976Sbostic (void)putc('\n', fp); 12911893Seric 13045976Sbostic (void)fflush(fp); 13145976Sbostic if (ferror(fp)) 13250092Sbostic err(FATAL, "temporary file write error"); 13345976Sbostic return(fd); 134579Sroot } 135579Sroot 13645976Sbostic deliver(fd, name) 13745976Sbostic int fd; 13845976Sbostic char *name; 139579Sroot { 14045976Sbostic struct stat sb; 14145976Sbostic struct passwd *pw; 14245976Sbostic int created, mbfd, nr, nw, off, rval; 14345976Sbostic char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; 14445976Sbostic off_t curoff, lseek(); 145579Sroot 14645976Sbostic /* 14745976Sbostic * Disallow delivery to unknown names -- special mailboxes can be 14845976Sbostic * handled in the sendmail aliases file. 14945976Sbostic */ 15045976Sbostic if (!(pw = getpwnam(name))) { 15150092Sbostic err(NOTFATAL, "unknown name: %s", name); 15245976Sbostic return(1); 15345976Sbostic } 154579Sroot 15545976Sbostic (void)sprintf(path, "%s/%s", _PATH_MAILDIR, name); 156579Sroot 15745976Sbostic if (!(created = lstat(path, &sb)) && 15845976Sbostic (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) { 15950092Sbostic err(NOTFATAL, "%s: linked file", path); 16045976Sbostic return(1); 16116731Sralph } 162579Sroot 163579Sroot /* 16445976Sbostic * There's a race here -- two processes think they both created 16545976Sbostic * the file. This means the file cannot be unlinked. 166579Sroot */ 16745976Sbostic if ((mbfd = 16845976Sbostic open(path, O_APPEND|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) { 16950092Sbostic err(NOTFATAL, "%s: %s", path, strerror(errno)); 17045976Sbostic return(1); 17145976Sbostic } 172579Sroot 17345976Sbostic rval = 0; 17445976Sbostic /* XXX: Open should allow flock'ing the file; see 4.4BSD. */ 17545976Sbostic if (flock(mbfd, LOCK_EX)) { 17650092Sbostic err(NOTFATAL, "%s: %s", path, strerror(errno)); 17745976Sbostic rval = 1; 17845976Sbostic goto bad; 17945976Sbostic } 180579Sroot 18145976Sbostic curoff = lseek(mbfd, 0L, SEEK_END); 18245976Sbostic (void)sprintf(biffmsg, "%s@%ld\n", name, curoff); 18345976Sbostic if (lseek(fd, 0L, SEEK_SET) == (off_t)-1) { 18450092Sbostic err(FATAL, "temporary file: %s", strerror(errno)); 18545976Sbostic rval = 1; 18645976Sbostic goto bad; 18745976Sbostic } 188579Sroot 18945976Sbostic while ((nr = read(fd, buf, sizeof(buf))) > 0) 19045976Sbostic for (off = 0; off < nr; nr -= nw, off += nw) 19145976Sbostic if ((nw = write(mbfd, buf + off, nr)) < 0) { 19250092Sbostic err(NOTFATAL, "%s: %s", path, strerror(errno)); 19345976Sbostic goto trunc; 19445976Sbostic } 19545976Sbostic if (nr < 0) { 19650092Sbostic err(FATAL, "temporary file: %s", strerror(errno)); 19745976Sbostic trunc: (void)ftruncate(mbfd, curoff); 19845976Sbostic rval = 1; 199579Sroot } 200579Sroot 20145976Sbostic /* 20245976Sbostic * Set the owner and group. Historically, binmail repeated this at 20345976Sbostic * each mail delivery. We no longer do this, assuming that if the 20445976Sbostic * ownership or permissions were changed there was a reason for doing 20545976Sbostic * so. 20645976Sbostic */ 20745976Sbostic bad: if (created) 20845976Sbostic (void)fchown(mbfd, pw->pw_uid, pw->pw_gid); 209579Sroot 21046541Sbostic (void)fsync(mbfd); /* Don't wait for update. */ 21146541Sbostic (void)close(mbfd); /* Implicit unlock. */ 212579Sroot 21345976Sbostic if (!rval) 21445976Sbostic notifybiff(biffmsg); 21545976Sbostic return(rval); 216579Sroot } 217579Sroot 21850092Sbostic void 21916731Sralph notifybiff(msg) 22016731Sralph char *msg; 22116731Sralph { 22216731Sralph static struct sockaddr_in addr; 22316731Sralph static int f = -1; 22445976Sbostic struct hostent *hp; 22545976Sbostic struct servent *sp; 22645976Sbostic int len; 22716731Sralph 22845976Sbostic if (!addr.sin_family) { 22945976Sbostic /* Be silent if biff service not available. */ 23045976Sbostic if (!(sp = getservbyname("biff", "udp"))) 23145976Sbostic return; 23245976Sbostic if (!(hp = gethostbyname("localhost"))) { 23350092Sbostic err(NOTFATAL, "localhost: %s", strerror(errno)); 23445976Sbostic return; 23516731Sralph } 23645976Sbostic addr.sin_family = hp->h_addrtype; 23745976Sbostic bcopy(hp->h_addr, &addr.sin_addr, hp->h_length); 23845976Sbostic addr.sin_port = sp->s_port; 23916731Sralph } 24045976Sbostic if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 24150092Sbostic err(NOTFATAL, "socket: %s", strerror(errno)); 24245976Sbostic return; 24316731Sralph } 24445976Sbostic len = strlen(msg) + 1; 24546672Sbostic if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)) 24646672Sbostic != len) 24750092Sbostic err(NOTFATAL, "sendto biff: %s", strerror(errno)); 24816731Sralph } 24916731Sralph 25050092Sbostic void 25145976Sbostic usage() 252579Sroot { 25350092Sbostic err(FATAL, "usage: mail.local [-f from] user ..."); 254579Sroot } 255579Sroot 25650092Sbostic #if __STDC__ 25750092Sbostic #include <stdarg.h> 25850092Sbostic #else 25950092Sbostic #include <varargs.h> 26050092Sbostic #endif 26150092Sbostic 26250092Sbostic void 26350092Sbostic #if __STDC__ 26450092Sbostic err(int isfatal, const char *fmt, ...) 26550092Sbostic #else 266*51674Sbostic err(isfatal, fmt, va_alist) 26746554Sbostic int isfatal; 26846554Sbostic char *fmt; 26950092Sbostic va_dcl 27050092Sbostic #endif 271579Sroot { 27245976Sbostic va_list ap; 27350092Sbostic #if __STDC__ 27446554Sbostic va_start(ap, fmt); 27550092Sbostic #else 27650092Sbostic va_start(ap); 27750092Sbostic #endif 27845976Sbostic vsyslog(LOG_ERR, fmt, ap); 27945976Sbostic va_end(ap); 28045976Sbostic if (isfatal) 28145976Sbostic exit(1); 282579Sroot } 283