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*54203Sbostic static char sccsid[] = "@(#)mail.local.c 5.8 (Berkeley) 06/22/92"; 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 struct passwd *pw; 4845976Sbostic int ch, fd, eval; 4945976Sbostic uid_t uid; 5045976Sbostic char *from; 51579Sroot 5245976Sbostic openlog("mail.local", LOG_PERROR, LOG_MAIL); 5316731Sralph 5445976Sbostic from = NULL; 5545976Sbostic while ((ch = getopt(argc, argv, "df:r:")) != EOF) 5645976Sbostic switch(ch) { 5745976Sbostic case 'd': /* backward compatible */ 5816731Sralph break; 5916731Sralph case 'f': 6045976Sbostic case 'r': /* backward compatible */ 6145976Sbostic if (from) 6250092Sbostic err(FATAL, "multiple -f options"); 6345976Sbostic from = optarg; 64579Sroot break; 6545976Sbostic case '?': 6616731Sralph default: 6745976Sbostic usage(); 6816731Sralph } 6945976Sbostic argc -= optind; 7045976Sbostic argv += optind; 71579Sroot 7245976Sbostic if (!*argv) 7345976Sbostic usage(); 74579Sroot 7545976Sbostic /* 7645976Sbostic * If from not specified, use the name from getlogin() if the 7745976Sbostic * uid matches, otherwise, use the name from the password file 7845976Sbostic * corresponding to the uid. 7945976Sbostic */ 8045976Sbostic uid = getuid(); 8146034Sbostic if (!from && (!(from = getlogin()) || 8246034Sbostic !(pw = getpwnam(from)) || pw->pw_uid != uid)) 8345976Sbostic from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; 84579Sroot 8545976Sbostic fd = store(from); 8645976Sbostic for (eval = 0; *argv; ++argv) 8745976Sbostic eval |= deliver(fd, *argv); 8845976Sbostic exit(eval); 89579Sroot } 90579Sroot 9145976Sbostic store(from) 9245976Sbostic char *from; 93579Sroot { 9445976Sbostic FILE *fp; 9545976Sbostic time_t tval; 9645976Sbostic int fd, eline; 9745976Sbostic char *tn, line[2048]; 98579Sroot 9947075Sdonn tn = strdup(_PATH_LOCTMP); 10045976Sbostic if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+"))) 10150092Sbostic err(FATAL, "unable to open temporary file"); 10245976Sbostic (void)unlink(tn); 10347075Sdonn free(tn); 104579Sroot 10545976Sbostic (void)time(&tval); 10645976Sbostic (void)fprintf(fp, "From %s %s", from, ctime(&tval)); 107579Sroot 10845976Sbostic line[0] = '\0'; 10945976Sbostic for (eline = 1; fgets(line, sizeof(line), stdin);) { 11045976Sbostic if (line[0] == '\n') 11145976Sbostic eline = 1; 11245976Sbostic else { 11345976Sbostic if (eline && line[0] == 'F' && !bcmp(line, "From ", 5)) 11445976Sbostic (void)putc('>', fp); 11545976Sbostic eline = 0; 11645976Sbostic } 11745976Sbostic (void)fprintf(fp, "%s", line); 11845976Sbostic if (ferror(fp)) 11945976Sbostic break; 120579Sroot } 121579Sroot 12245976Sbostic /* If message not newline terminated, need an extra. */ 12345976Sbostic if (!index(line, '\n')) 12445976Sbostic (void)putc('\n', fp); 12545976Sbostic /* Output a newline; note, empty messages are allowed. */ 12645976Sbostic (void)putc('\n', fp); 12711893Seric 12845976Sbostic (void)fflush(fp); 12945976Sbostic if (ferror(fp)) 13050092Sbostic err(FATAL, "temporary file write error"); 13145976Sbostic return(fd); 132579Sroot } 133579Sroot 13445976Sbostic deliver(fd, name) 13545976Sbostic int fd; 13645976Sbostic char *name; 137579Sroot { 13845976Sbostic struct stat sb; 13945976Sbostic struct passwd *pw; 14045976Sbostic int created, mbfd, nr, nw, off, rval; 14145976Sbostic char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; 142*54203Sbostic off_t curoff; 143579Sroot 14445976Sbostic /* 14545976Sbostic * Disallow delivery to unknown names -- special mailboxes can be 14645976Sbostic * handled in the sendmail aliases file. 14745976Sbostic */ 14845976Sbostic if (!(pw = getpwnam(name))) { 14950092Sbostic err(NOTFATAL, "unknown name: %s", name); 15045976Sbostic return(1); 15145976Sbostic } 152579Sroot 15345976Sbostic (void)sprintf(path, "%s/%s", _PATH_MAILDIR, name); 154579Sroot 15545976Sbostic if (!(created = lstat(path, &sb)) && 15645976Sbostic (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) { 15750092Sbostic err(NOTFATAL, "%s: linked file", path); 15845976Sbostic return(1); 15916731Sralph } 160579Sroot 161579Sroot /* 16245976Sbostic * There's a race here -- two processes think they both created 16345976Sbostic * the file. This means the file cannot be unlinked. 164579Sroot */ 16545976Sbostic if ((mbfd = 16645976Sbostic open(path, O_APPEND|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) { 16750092Sbostic err(NOTFATAL, "%s: %s", path, strerror(errno)); 16845976Sbostic return(1); 16945976Sbostic } 170579Sroot 17145976Sbostic rval = 0; 17245976Sbostic /* XXX: Open should allow flock'ing the file; see 4.4BSD. */ 17345976Sbostic if (flock(mbfd, LOCK_EX)) { 17450092Sbostic err(NOTFATAL, "%s: %s", path, strerror(errno)); 17545976Sbostic rval = 1; 17645976Sbostic goto bad; 17745976Sbostic } 178579Sroot 179*54203Sbostic curoff = lseek(mbfd, (off_t)0, SEEK_END); 180*54203Sbostic (void)sprintf(biffmsg, "%s@%qd\n", name, curoff); 181*54203Sbostic if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { 18250092Sbostic err(FATAL, "temporary file: %s", strerror(errno)); 18345976Sbostic rval = 1; 18445976Sbostic goto bad; 18545976Sbostic } 186579Sroot 18745976Sbostic while ((nr = read(fd, buf, sizeof(buf))) > 0) 18845976Sbostic for (off = 0; off < nr; nr -= nw, off += nw) 18945976Sbostic if ((nw = write(mbfd, buf + off, nr)) < 0) { 19050092Sbostic err(NOTFATAL, "%s: %s", path, strerror(errno)); 19145976Sbostic goto trunc; 19245976Sbostic } 19345976Sbostic if (nr < 0) { 19450092Sbostic err(FATAL, "temporary file: %s", strerror(errno)); 19545976Sbostic trunc: (void)ftruncate(mbfd, curoff); 19645976Sbostic rval = 1; 197579Sroot } 198579Sroot 19945976Sbostic /* 20045976Sbostic * Set the owner and group. Historically, binmail repeated this at 20145976Sbostic * each mail delivery. We no longer do this, assuming that if the 20245976Sbostic * ownership or permissions were changed there was a reason for doing 20345976Sbostic * so. 20445976Sbostic */ 20545976Sbostic bad: if (created) 20645976Sbostic (void)fchown(mbfd, pw->pw_uid, pw->pw_gid); 207579Sroot 20846541Sbostic (void)fsync(mbfd); /* Don't wait for update. */ 20946541Sbostic (void)close(mbfd); /* Implicit unlock. */ 210579Sroot 21145976Sbostic if (!rval) 21245976Sbostic notifybiff(biffmsg); 21345976Sbostic return(rval); 214579Sroot } 215579Sroot 21650092Sbostic void 21716731Sralph notifybiff(msg) 21816731Sralph char *msg; 21916731Sralph { 22016731Sralph static struct sockaddr_in addr; 22116731Sralph static int f = -1; 22245976Sbostic struct hostent *hp; 22345976Sbostic struct servent *sp; 22445976Sbostic int len; 22516731Sralph 22645976Sbostic if (!addr.sin_family) { 22745976Sbostic /* Be silent if biff service not available. */ 22845976Sbostic if (!(sp = getservbyname("biff", "udp"))) 22945976Sbostic return; 23045976Sbostic if (!(hp = gethostbyname("localhost"))) { 23150092Sbostic err(NOTFATAL, "localhost: %s", strerror(errno)); 23245976Sbostic return; 23316731Sralph } 23445976Sbostic addr.sin_family = hp->h_addrtype; 23545976Sbostic bcopy(hp->h_addr, &addr.sin_addr, hp->h_length); 23645976Sbostic addr.sin_port = sp->s_port; 23716731Sralph } 23845976Sbostic if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 23950092Sbostic err(NOTFATAL, "socket: %s", strerror(errno)); 24045976Sbostic return; 24116731Sralph } 24245976Sbostic len = strlen(msg) + 1; 24346672Sbostic if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)) 24446672Sbostic != len) 24550092Sbostic err(NOTFATAL, "sendto biff: %s", strerror(errno)); 24616731Sralph } 24716731Sralph 24850092Sbostic void 24945976Sbostic usage() 250579Sroot { 25150092Sbostic err(FATAL, "usage: mail.local [-f from] user ..."); 252579Sroot } 253579Sroot 25450092Sbostic #if __STDC__ 25550092Sbostic #include <stdarg.h> 25650092Sbostic #else 25750092Sbostic #include <varargs.h> 25850092Sbostic #endif 25950092Sbostic 26050092Sbostic void 26150092Sbostic #if __STDC__ 26250092Sbostic err(int isfatal, const char *fmt, ...) 26350092Sbostic #else 26451674Sbostic err(isfatal, fmt, va_alist) 26546554Sbostic int isfatal; 26646554Sbostic char *fmt; 26750092Sbostic va_dcl 26850092Sbostic #endif 269579Sroot { 27045976Sbostic va_list ap; 27150092Sbostic #if __STDC__ 27246554Sbostic va_start(ap, fmt); 27350092Sbostic #else 27450092Sbostic va_start(ap); 27550092Sbostic #endif 27645976Sbostic vsyslog(LOG_ERR, fmt, ap); 27745976Sbostic va_end(ap); 27845976Sbostic if (isfatal) 27945976Sbostic exit(1); 280579Sroot } 281