1*45976Sbostic /*- 2*45976Sbostic * Copyright (c) 1990 The Regents of the University of California. 3*45976Sbostic * All rights reserved. 4*45976Sbostic * 5*45976Sbostic * %sccs.include.redist.c% 6*45976Sbostic */ 7*45976Sbostic 814465Ssam #ifndef lint 9*45976Sbostic char copyright[] = 10*45976Sbostic "@(#) Copyright (c) 1990 The Regents of the University of California.\n\ 11*45976Sbostic All rights reserved.\n"; 12*45976Sbostic #endif /* not lint */ 1314465Ssam 14*45976Sbostic #ifndef lint 15*45976Sbostic static char sccsid[] = "@(#)mail.local.c 4.38 (Berkeley) 01/17/91"; 16*45976Sbostic #endif /* not lint */ 17*45976Sbostic 1829763Skarels #include <sys/param.h> 1916731Sralph #include <sys/stat.h> 2016731Sralph #include <sys/file.h> 21*45976Sbostic #include <sys/socket.h> 22*45976Sbostic #include <sys/syslog.h> 23*45976Sbostic #include <sys/errno.h> 24*45976Sbostic #include <netinet/in.h> 25*45976Sbostic #include <netdb.h> 26*45976Sbostic #include <pwd.h> 27*45976Sbostic #include <time.h> 28*45976Sbostic #include <varargs.h> 29579Sroot #include <stdio.h> 30*45976Sbostic #include <string.h> 31*45976Sbostic #include <stdlib.h> 3237465Sbostic #include "pathnames.h" 33579Sroot 34*45976Sbostic #define FATAL 1 35*45976Sbostic #define NOTFATAL 0 36579Sroot 37579Sroot main(argc, argv) 38*45976Sbostic int argc; 3916731Sralph char **argv; 40579Sroot { 41*45976Sbostic extern int optind; 42*45976Sbostic extern char *optarg; 43*45976Sbostic struct passwd *pw; 44*45976Sbostic int ch, fd, eval; 45*45976Sbostic uid_t uid; 46*45976Sbostic char *from; 47579Sroot 48*45976Sbostic openlog("mail.local", LOG_PERROR, LOG_MAIL); 4916731Sralph 50*45976Sbostic from = NULL; 51*45976Sbostic while ((ch = getopt(argc, argv, "df:r:")) != EOF) 52*45976Sbostic switch(ch) { 53*45976Sbostic case 'd': /* backward compatible */ 5416731Sralph break; 5516731Sralph case 'f': 56*45976Sbostic case 'r': /* backward compatible */ 57*45976Sbostic if (from) 58*45976Sbostic error(FATAL, "multiple -f options."); 59*45976Sbostic from = optarg; 60579Sroot break; 61*45976Sbostic case '?': 6216731Sralph default: 63*45976Sbostic usage(); 6416731Sralph } 65*45976Sbostic argc -= optind; 66*45976Sbostic argv += optind; 67579Sroot 68*45976Sbostic if (!*argv) 69*45976Sbostic usage(); 70579Sroot 71*45976Sbostic /* 72*45976Sbostic * If from not specified, use the name from getlogin() if the 73*45976Sbostic * uid matches, otherwise, use the name from the password file 74*45976Sbostic * corresponding to the uid. 75*45976Sbostic */ 76*45976Sbostic uid = getuid(); 77*45976Sbostic if (!from || !(from = getlogin()) || 78*45976Sbostic !(pw = getpwnam(from)) || pw->pw_uid != uid) 79*45976Sbostic from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; 80579Sroot 81*45976Sbostic fd = store(from); 82*45976Sbostic for (eval = 0; *argv; ++argv) 83*45976Sbostic eval |= deliver(fd, *argv); 84*45976Sbostic exit(eval); 85579Sroot } 86579Sroot 87*45976Sbostic store(from) 88*45976Sbostic char *from; 89579Sroot { 90*45976Sbostic FILE *fp; 91*45976Sbostic time_t tval; 92*45976Sbostic int fd, eline; 93*45976Sbostic char *tn, line[2048]; 94579Sroot 95*45976Sbostic tn = _PATH_LOCTMP; 96*45976Sbostic if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+"))) 97*45976Sbostic error(FATAL, "unable to open temporary file."); 98*45976Sbostic (void)unlink(tn); 99579Sroot 100*45976Sbostic (void)time(&tval); 101*45976Sbostic (void)fprintf(fp, "From %s %s", from, ctime(&tval)); 102579Sroot 103*45976Sbostic line[0] = '\0'; 104*45976Sbostic for (eline = 1; fgets(line, sizeof(line), stdin);) { 105*45976Sbostic if (line[0] == '\n') 106*45976Sbostic eline = 1; 107*45976Sbostic else { 108*45976Sbostic if (eline && line[0] == 'F' && !bcmp(line, "From ", 5)) 109*45976Sbostic (void)putc('>', fp); 110*45976Sbostic eline = 0; 111*45976Sbostic } 112*45976Sbostic (void)fprintf(fp, "%s", line); 113*45976Sbostic if (ferror(fp)) 114*45976Sbostic break; 115579Sroot } 116579Sroot 117*45976Sbostic /* If message not newline terminated, need an extra. */ 118*45976Sbostic if (!index(line, '\n')) 119*45976Sbostic (void)putc('\n', fp); 120*45976Sbostic /* Output a newline; note, empty messages are allowed. */ 121*45976Sbostic (void)putc('\n', fp); 12211893Seric 123*45976Sbostic (void)fflush(fp); 124*45976Sbostic if (ferror(fp)) 125*45976Sbostic error(FATAL, "temporary file write error."); 126*45976Sbostic return(fd); 127579Sroot } 128579Sroot 129*45976Sbostic deliver(fd, name) 130*45976Sbostic int fd; 131*45976Sbostic char *name; 132579Sroot { 133*45976Sbostic struct stat sb; 134*45976Sbostic struct passwd *pw; 135*45976Sbostic int created, mbfd, nr, nw, off, rval; 136*45976Sbostic char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; 137*45976Sbostic off_t curoff, lseek(); 138579Sroot 139*45976Sbostic /* 140*45976Sbostic * Disallow delivery to unknown names -- special mailboxes can be 141*45976Sbostic * handled in the sendmail aliases file. 142*45976Sbostic */ 143*45976Sbostic if (!(pw = getpwnam(name))) { 144*45976Sbostic error(NOTFATAL, "unknown name: %s.", name); 145*45976Sbostic return(1); 146*45976Sbostic } 147579Sroot 148*45976Sbostic (void)sprintf(path, "%s/%s", _PATH_MAILDIR, name); 149579Sroot 150*45976Sbostic if (!(created = lstat(path, &sb)) && 151*45976Sbostic (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) { 152*45976Sbostic error(NOTFATAL, "%s: linked file.", path); 153*45976Sbostic return(1); 15416731Sralph } 155579Sroot 156579Sroot /* 157*45976Sbostic * There's a race here -- two processes think they both created 158*45976Sbostic * the file. This means the file cannot be unlinked. 159579Sroot */ 160*45976Sbostic if ((mbfd = 161*45976Sbostic open(path, O_APPEND|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) { 162*45976Sbostic error(NOTFATAL, "%s: %s.", path, strerror(errno)); 163*45976Sbostic return(1); 164*45976Sbostic } 165579Sroot 166*45976Sbostic rval = 0; 167*45976Sbostic /* XXX: Open should allow flock'ing the file; see 4.4BSD. */ 168*45976Sbostic if (flock(mbfd, LOCK_EX)) { 169*45976Sbostic error(NOTFATAL, "%s: %s.", path, strerror(errno)); 170*45976Sbostic rval = 1; 171*45976Sbostic goto bad; 172*45976Sbostic } 173579Sroot 174*45976Sbostic curoff = lseek(mbfd, 0L, SEEK_END); 175*45976Sbostic (void)sprintf(biffmsg, "%s@%ld\n", name, curoff); 176*45976Sbostic if (lseek(fd, 0L, SEEK_SET) == (off_t)-1) { 177*45976Sbostic error(FATAL, "temporary file: %s.", strerror(errno)); 178*45976Sbostic rval = 1; 179*45976Sbostic goto bad; 180*45976Sbostic } 181579Sroot 182*45976Sbostic while ((nr = read(fd, buf, sizeof(buf))) > 0) 183*45976Sbostic for (off = 0; off < nr; nr -= nw, off += nw) 184*45976Sbostic if ((nw = write(mbfd, buf + off, nr)) < 0) { 185*45976Sbostic error(NOTFATAL, 186*45976Sbostic "%s: %s.", path, strerror(errno)); 187*45976Sbostic goto trunc; 188*45976Sbostic } 189*45976Sbostic if (nr < 0) { 190*45976Sbostic error(FATAL, "temporary file: %s.", strerror(errno)); 191*45976Sbostic trunc: (void)ftruncate(mbfd, curoff); 192*45976Sbostic rval = 1; 193579Sroot } 194579Sroot 195*45976Sbostic /* 196*45976Sbostic * Set the owner and group. Historically, binmail repeated this at 197*45976Sbostic * each mail delivery. We no longer do this, assuming that if the 198*45976Sbostic * ownership or permissions were changed there was a reason for doing 199*45976Sbostic * so. 200*45976Sbostic */ 201*45976Sbostic bad: if (created) 202*45976Sbostic (void)fchown(mbfd, pw->pw_uid, pw->pw_gid); 203579Sroot 204*45976Sbostic /* Implicit unlock. */ 205*45976Sbostic (void)close(mbfd); 206579Sroot 207*45976Sbostic if (!rval) 208*45976Sbostic notifybiff(biffmsg); 209*45976Sbostic return(rval); 210579Sroot } 211579Sroot 21216731Sralph notifybiff(msg) 21316731Sralph char *msg; 21416731Sralph { 21516731Sralph static struct sockaddr_in addr; 21616731Sralph static int f = -1; 217*45976Sbostic struct hostent *hp; 218*45976Sbostic struct servent *sp; 219*45976Sbostic int len; 22016731Sralph 221*45976Sbostic if (!addr.sin_family) { 222*45976Sbostic /* Be silent if biff service not available. */ 223*45976Sbostic if (!(sp = getservbyname("biff", "udp"))) 224*45976Sbostic return; 225*45976Sbostic if (!(hp = gethostbyname("localhost"))) { 226*45976Sbostic error(NOTFATAL, "localhost: %s.", strerror(errno)); 227*45976Sbostic return; 22816731Sralph } 229*45976Sbostic addr.sin_family = hp->h_addrtype; 230*45976Sbostic bcopy(hp->h_addr, &addr.sin_addr, hp->h_length); 231*45976Sbostic addr.sin_port = sp->s_port; 23216731Sralph } 233*45976Sbostic if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 234*45976Sbostic error(NOTFATAL, "socket: %s.", strerror(errno)); 235*45976Sbostic return; 23616731Sralph } 237*45976Sbostic len = strlen(msg) + 1; 238*45976Sbostic if (sendto(f, msg, len, 0, &addr, sizeof(addr)) != len) 239*45976Sbostic error(NOTFATAL, "sendto biff: %s.", strerror(errno)); 24016731Sralph } 24116731Sralph 242*45976Sbostic usage() 243579Sroot { 244*45976Sbostic error(FATAL, "usage: mail.local [-f from] user ..."); 245579Sroot } 246579Sroot 247*45976Sbostic /* VARARGS */ 248*45976Sbostic error(va_alist) 249*45976Sbostic va_dcl 250579Sroot { 251*45976Sbostic va_list ap; 252*45976Sbostic int isfatal; 253*45976Sbostic char *fmt; 254579Sroot 255*45976Sbostic va_start(ap); 256*45976Sbostic isfatal = va_arg(ap, int); 257*45976Sbostic fmt = va_arg(ap, char *); 258*45976Sbostic vsyslog(LOG_ERR, fmt, ap); 259*45976Sbostic va_end(ap); 260*45976Sbostic if (isfatal) 261*45976Sbostic exit(1); 262579Sroot } 263