BSD 4_4 release
[unix-history] / usr / src / libexec / mail.local / mail.local.c
index e2c9a12..2034665 100644 (file)
+/*-
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
 #ifndef lint
 #ifndef lint
-static char sccsid[] = "@(#)mail.local.c       4.28 (Berkeley) %G%";
-#endif
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mail.local.c       8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
 
 #include <sys/param.h>
 #include <sys/stat.h>
 
 #include <sys/param.h>
 #include <sys/stat.h>
-#include <sys/file.h>
+#include <sys/socket.h>
 
 
-#include <ctype.h>
-#include <stdio.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
 #include <pwd.h>
 #include <pwd.h>
-#include <utmp.h>
-#include <signal.h>
-#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sysexits.h>
 #include <sysexits.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
 
 
-#define SENDMAIL       "/usr/lib/sendmail"
-
-       /* copylet flags */
-#define REMOTE         1               /* remote mail, add rmtmsg */
-#define ORDINARY       2
-#define ZAP            3               /* zap header and trailing empty line */
-#define        FORWARD         4
-
-#define        LSIZE           256
-#define        MAXLET          300             /* maximum number of letters */
-#define        MAILMODE        0600            /* mode of created mail */
-
-char   line[LSIZE];
-char   resp[LSIZE];
-struct let {
-       long    adr;
-       char    change;
-} let[MAXLET];
-int    nlet    = 0;
-char   lfil[50];
-long   iop, time();
-char   *getenv();
-char   *index();
-char   lettmp[] = "/tmp/maXXXXX";
-char   maildir[] = "/usr/spool/mail/";
-char   mailfile[] = "/usr/spool/mail/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
-char   dead[] = "dead.letter";
-char   forwmsg[] = " forwarded\n";
-FILE   *tmpf;
-FILE   *malf;
-char   my_name[60];
-char   *getlogin();
-int    error;
-int    changed;
-int    forward;
-char   from[] = "From ";
-long   ftell();
-int    delex();
-char   *ctime();
-int    flgf;
-int    flgp;
-int    delflg = 1;
-int    hseqno;
-jmp_buf        sjbuf;
-int    rmail;
+#include "pathnames.h"
 
 
-main(argc, argv)
-char **argv;
-{
-       register i;
-       char *name;
-       struct passwd *pwent;
-
-       name = getlogin();
-       if (name == NULL || *name == '\0') {
-               pwent = getpwuid(getuid());
-               if (pwent==NULL)
-                       name = "???";
-               else
-                       name = pwent->pw_name;
-       }
-       else {
-               pwent = getpwnam(my_name);
-               if ( getuid() != pwent->pw_uid) {
-                       pwent = getpwuid(getuid());
-                       name = pwent->pw_name;
-               }
-       }
-       strncpy(my_name, name, sizeof(my_name)-1);
-       if (setjmp(sjbuf))
-               done();
-       for (i=SIGHUP; i<=SIGTERM; i++)
-               setsig(i, delex);
-       i = mkstemp(lettmp);
-       tmpf = fdopen(i, "r+w");
-       if (i < 0 || tmpf == NULL)
-               panic("mail: %s: cannot open for writing", lettmp);
-       /*
-        * This protects against others reading mail from temp file and
-        * if we exit, the file will be deleted already.
-        */
-       unlink(lettmp);
-       if (argv[0][0] == 'r')
-               rmail++;
-       if (argv[0][0] != 'r' &&        /* no favors for rmail*/
-          (argc == 1 || argv[1][0] == '-' && !any(argv[1][1], "rhd")))
-               printmail(argc, argv);
-       else
-               bulkmail(argc, argv);
-       done();
-}
+int eval = EX_OK;                      /* sysexits.h error value. */
 
 
-setsig(i, f)
-int i;
-int (*f)();
-{
-       if (signal(i, SIG_IGN) != SIG_IGN)
-               signal(i, f);
-}
+void           deliver __P((int, char *));
+void           e_to_sys __P((int));
+__dead void    err __P((const char *, ...));
+void           notifybiff __P((char *));
+int            store __P((char *));
+void           usage __P((void));
+void           vwarn __P((const char *, _BSD_VA_LIST_));
+void           warn __P((const char *, ...));
 
 
-any(c, str)
-       register int c;
-       register char *str;
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
 {
 {
+       struct passwd *pw;
+       int ch, fd;
+       uid_t uid;
+       char *from;
 
 
-       while (*str)
-               if (c == *str++)
-                       return(1);
-       return(0);
-}
-
-printmail(argc, argv)
-       char **argv;
-{
-       int flg, i, j, print;
-       char *p, *getarg();
-       struct stat statb;
-
-       setuid(getuid());
-       cat(mailfile, maildir, my_name);
-#ifdef notdef
-       if (stat(mailfile, &statb) >= 0
-           && (statb.st_mode & S_IFMT) == S_IFDIR) {
-               strcat(mailfile, "/");
-               strcat(mailfile, my_name);
-       }
-#endif
-       for (; argc > 1; argv++, argc--) {
-               if (argv[1][0] != '-')
-                       break;
-               switch (argv[1][1]) {
+       openlog("mail.local", 0, LOG_MAIL);
 
 
-               case 'p':
-                       flgp++;
-                       /* fall thru... */
-               case 'q':
-                       delflg = 0;
+       from = NULL;
+       while ((ch = getopt(argc, argv, "df:r:")) != EOF)
+               switch(ch) {
+               case 'd':               /* Backward compatible. */
                        break;
                        break;
-
                case 'f':
                case 'f':
-                       if (argc >= 3) {
-                               strcpy(mailfile, argv[2]);
-                               argv++, argc--;
+               case 'r':               /* Backward compatible. */
+                       if (from != NULL) {
+                               warn("multiple -f options");
+                               usage();
                        }
                        }
+                       from = optarg;
                        break;
                        break;
-
-               case 'b':
-                       forward = 1;
-                       break;
-
+               case '?':
                default:
                default:
-                       panic("unknown option %c", argv[1][1]);
-                       /*NOTREACHED*/
-               }
-       }
-       malf = fopen(mailfile, "r");
-       if (malf == NULL) {
-               printf("No mail.\n");
-               return;
-       }
-       flock(fileno(malf), LOCK_SH);
-       copymt(malf, tmpf);
-       fclose(malf);                   /* implicit unlock */
-       fseek(tmpf, 0, L_SET);
-
-       changed = 0;
-       print = 1;
-       for (i = 0; i < nlet; ) {
-               j = forward ? i : nlet - i - 1;
-               if (setjmp(sjbuf)) {
-                       print = 0;
-               } else {
-                       if (print)
-                               copylet(j, stdout, ORDINARY);
-                       print = 1;
-               }
-               if (flgp) {
-                       i++;
-                       continue;
+                       usage();
                }
                }
-               setjmp(sjbuf);
-               fputs("? ", stdout);
-               fflush(stdout);
-               if (fgets(resp, LSIZE, stdin) == NULL)
-                       break;
-               switch (resp[0]) {
+       argc -= optind;
+       argv += optind;
 
 
-               default:
-                       printf("usage\n");
-               case '?':
-                       print = 0;
-                       printf("q\tquit\n");
-                       printf("x\texit without changing mail\n");
-                       printf("p\tprint\n");
-                       printf("s[file]\tsave (default mbox)\n");
-                       printf("w[file]\tsame without header\n");
-                       printf("-\tprint previous\n");
-                       printf("d\tdelete\n");
-                       printf("+\tnext (no delete)\n");
-                       printf("m user\tmail to user\n");
-                       printf("! cmd\texecute cmd\n");
-                       break;
+       if (!*argv)
+               usage();
 
 
-               case '+':
-               case 'n':
-               case '\n':
-                       i++;
-                       break;
-               case 'x':
-                       changed = 0;
-               case 'q':
-                       goto donep;
-               case 'p':
-                       break;
-               case '^':
-               case '-':
-                       if (--i < 0)
-                               i = 0;
-                       break;
-               case 'y':
-               case 'w':
-               case 's':
-                       flg = 0;
-                       if (resp[1] != '\n' && resp[1] != ' ') {
-                               printf("illegal\n");
-                               flg++;
-                               print = 0;
-                               continue;
-                       }
-                       if (resp[1] == '\n' || resp[1] == '\0') {
-                               p = getenv("HOME");
-                               if (p != 0)
-                                       cat(resp+1, p, "/mbox");
-                               else
-                                       cat(resp+1, "", "mbox");
-                       }
-                       for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) {
-                               malf = fopen(lfil, "a");
-                               if (malf == NULL) {
-                                       printf("mail: %s: cannot append\n",
-                                           lfil);
-                                       flg++;
-                                       continue;
-                               }
-                               copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY);
-                               fclose(malf);
-                       }
-                       if (flg)
-                               print = 0;
-                       else {
-                               let[j].change = 'd';
-                               changed++;
-                               i++;
-                       }
-                       break;
-               case 'm':
-                       flg = 0;
-                       if (resp[1] == '\n' || resp[1] == '\0') {
-                               i++;
-                               continue;
-                       }
-                       if (resp[1] != ' ') {
-                               printf("invalid command\n");
-                               flg++;
-                               print = 0;
-                               continue;
-                       }
-                       for (p = resp+1; (p = getarg(lfil, p)) != NULL; )
-                               if (!sendmail(j, lfil, my_name))
-                                       flg++;
-                       if (flg)
-                               print = 0;
-                       else {
-                               let[j].change = 'd';
-                               changed++;
-                               i++;
-                       }
-                       break;
-               case '!':
-                       system(resp+1);
-                       printf("!\n");
-                       print = 0;
-                       break;
-               case 'd':
-                       let[j].change = 'd';
-                       changed++;
-                       i++;
-                       if (resp[1] == 'q')
-                               goto donep;
-                       break;
-               }
-       }
-   donep:
-       if (changed)
-               copyback();
-}
+       /*
+        * If from not specified, use the name from getlogin() if the
+        * uid matches, otherwise, use the name from the password file
+        * corresponding to the uid.
+        */
+       uid = getuid();
+       if (!from && (!(from = getlogin()) ||
+           !(pw = getpwnam(from)) || pw->pw_uid != uid))
+               from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
 
 
-/* copy temp or whatever back to /usr/spool/mail */
-copyback()
-{
-       register i, c;
-       int fd, new = 0, oldmask;
-       struct stat stbuf;
-
-#define        mask(s) (1 << ((s) - 1))
-       oldmask = sigblock(mask(SIGINT)|mask(SIGHUP)|mask(SIGQUIT));
-#undef mask
-       fd = open(mailfile, O_RDWR | O_CREAT, MAILMODE);
-       if (fd >= 0) {
-               flock(fd, LOCK_EX);
-               malf = fdopen(fd, "r+w");
-       }
-       if (fd < 0 || malf == NULL)
-               panic("can't rewrite %s", lfil);
-       fstat(fd, &stbuf);
-       if (stbuf.st_size != let[nlet].adr) {   /* new mail has arrived */
-               fseek(malf, let[nlet].adr, L_SET);
-               fseek(tmpf, let[nlet].adr, L_SET);
-               while ((c = getc(malf)) != EOF)
-                       putc(c, tmpf);
-               let[++nlet].adr = stbuf.st_size;
-               new = 1;
-               fseek(malf, 0, L_SET);
-       }
-       ftruncate(fd, 0);
-       for (i = 0; i < nlet; i++)
-               if (let[i].change != 'd')
-                       copylet(i, malf, ORDINARY);
-       fclose(malf);           /* implict unlock */
-       if (new)
-               printf("New mail has arrived.\n");
-       sigsetmask(oldmask);
+       /*
+        * There is no way to distinguish the error status of one delivery
+        * from the rest of the deliveries.  So, if we failed hard on one
+        * or more deliveries, but had no failures on any of the others, we
+        * return a hard failure.  If we failed temporarily on one or more
+        * deliveries, we return a temporary failure regardless of the other
+        * failures.  This results in the delivery being reattempted later
+        * at the expense of repeated failures and multiple deliveries.
+        */
+       for (fd = store(from); *argv; ++argv)
+               deliver(fd, *argv);
+       exit(eval);
 }
 
 }
 
-/* copy mail (f1) to temp (f2) */
-copymt(f1, f2)
-       FILE *f1, *f2;
+int
+store(from)
+       char *from;
 {
 {
-       long nextadr;
-
-       nlet = nextadr = 0;
-       let[0].adr = 0;
-       while (fgets(line, LSIZE, f1) != NULL) {
-               if (isfrom(line))
-                       let[nlet++].adr = nextadr;
-               nextadr += strlen(line);
-               fputs(line, f2);
+       FILE *fp;
+       time_t tval;
+       int fd, eline;
+       char *tn, line[2048];
+
+       tn = strdup(_PATH_LOCTMP);
+       if ((fd = mkstemp(tn)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
+               e_to_sys(errno);
+               err("unable to open temporary file");
        }
        }
-       let[nlet].adr = nextadr;        /* last plus 1 */
-}
+       (void)unlink(tn);
+       free(tn);
 
 
-copylet(n, f, type)
-       FILE *f;
-{
-       int ch;
-       long k;
-       char hostname[MAXHOSTNAMELEN];
-
-       fseek(tmpf, let[n].adr, L_SET);
-       k = let[n+1].adr - let[n].adr;
-       while (k-- > 1 && (ch = getc(tmpf)) != '\n')
-               if (type != ZAP)
-                       putc(ch, f);
-       switch (type) {
-
-       case REMOTE:
-               gethostname(hostname, sizeof (hostname));
-               fprintf(f, " remote from %s\n", hostname);
-               break;
-
-       case FORWARD:
-               fprintf(f, forwmsg);
-               break;
+       (void)time(&tval);
+       (void)fprintf(fp, "From %s %s", from, ctime(&tval));
 
 
-       case ORDINARY:
-               putc(ch, f);
-               break;
+       line[0] = '\0';
+       for (eline = 1; fgets(line, sizeof(line), stdin);) {
+               if (line[0] == '\n')
+                       eline = 1;
+               else {
+                       if (eline && line[0] == 'F' &&
+                           !memcmp(line, "From ", 5))
+                               (void)putc('>', fp);
+                       eline = 0;
+               }
+               (void)fprintf(fp, "%s", line);
+               if (ferror(fp)) {
+                       e_to_sys(errno);
+                       err("temporary file write error");
+               }
+       }
 
 
-       case ZAP:
-               break;
+       /* If message not newline terminated, need an extra. */
+       if (!strchr(line, '\n'))
+               (void)putc('\n', fp);
+       /* Output a newline; note, empty messages are allowed. */
+       (void)putc('\n', fp);
 
 
-       default:
-               panic("Bad letter type %d to copylet.", type);
-       }
-       while (k-- > 1) {
-               ch = getc(tmpf);
-               putc(ch, f);
+       if (fflush(fp) == EOF || ferror(fp)) {
+               e_to_sys(errno);
+               err("temporary file write error");
        }
        }
-       if (type != ZAP || ch != '\n')
-               putc(getc(tmpf), f);
+       return (fd);
 }
 
 }
 
-isfrom(lp)
-register char *lp;
+void
+deliver(fd, name)
+       int fd;
+       char *name;
 {
 {
-       register char *p;
-
-       for (p = from; *p; )
-               if (*lp++ != *p++)
-                       return(0);
-       return(1);
-}
+       struct stat sb;
+       struct passwd *pw;
+       int mbfd, nr, nw, off;
+       char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
+       off_t curoff;
 
 
-bulkmail(argc, argv)
-char **argv;
-{
-       char *truename;
-       int first;
-       register char *cp;
-       char *newargv[1000];
-       register char **ap;
-       register char **vp;
-       int dflag;
-
-       dflag = 0;
-       delflg = 0;
-       if (argc < 1) {
-               fprintf(stderr, "puke\n");
+       /*
+        * Disallow delivery to unknown names -- special mailboxes can be
+        * handled in the sendmail aliases file.
+        */
+       if (!(pw = getpwnam(name))) {
+               if (eval != EX_TEMPFAIL)
+                       eval = EX_UNAVAILABLE;
+               warn("unknown name: %s", name);
                return;
        }
                return;
        }
-       for (vp = argv, ap = newargv + 1; (*ap = *vp++) != 0; ap++)
-               if (ap[0][0] == '-' && ap[0][1] == 'd')
-                       dflag++;
-       if (!dflag) {
-               /* give it to sendmail, rah rah! */
-               unlink(lettmp);
-               ap = newargv+1;
-               if (rmail)
-                       *ap-- = "-s";
-               *ap = "-sendmail";
-               setuid(getuid());
-               execv(SENDMAIL, ap);
-               perror(SENDMAIL);
-               exit(EX_UNAVAILABLE);
-       }
 
 
-       truename = 0;
-       line[0] = '\0';
+       (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
 
        /*
 
        /*
-        * When we fall out of this, argv[1] should be first name,
-        * argc should be number of names + 1.
+        * If the mailbox is a linked or a symlink, fail.
+        *
+        * If we created the mailbox, set the owner/group.  If that fails,
+        * just return.  Another process may have already opened it, so we
+        * can't unlink it.  Historically, binmail set the owner/group at
+        * each mail delivery.  We no longer do this, assuming that if the
+        * ownership or permissions were changed there was a reason.
+        *
+        * XXX
+        * open(2) should support flock'ing the file.
         */
         */
-
-       while (argc > 1 && *argv[1] == '-') {
-               cp = *++argv;
-               argc--;
-               switch (cp[1]) {
-               case 'r':
-                       if (argc <= 1)
-                               usage();
-                       if (strcmp(my_name, "root") == 0 ||
-                           strcmp(my_name, "daemon") == 0 ||
-                           strcmp(my_name, "network") == 0 ||
-                           strcmp(my_name, "uucp")) {
-                               gaver++;
-                               strcpy(truename, argv[1]);
-                               fgets(line, LSIZE, stdin);
-                               if (strncmp("From", line, 4) == 0)
-                                       line[0] = '\0';
-                       }
-                       argv++;
-                       argc--;
-                       break;
-
-               case 'h':
-                       if (argc <= 1)
-                               usage();
-                       hseqno = atoi(argv[1]);
-                       argv++;
-                       argc--;
-                       break;
-
-               case 'd':
-                       break;
-               
-               default:
-                       usage();
-               }
-       }
-       if (argc <= 1)
-               usage();
-       if (truename == 0)
-               truename = my_name;
-       time(&iop);
-       fprintf(tmpf, "%s%s %s", from, truename, ctime(&iop));
-       iop = ftell(tmpf);
-       flgf = first = 1;
-       for (;;) {
-               if (first) {
-                       first = 0;
-                       if (*line == '\0' && fgets(line, LSIZE, stdin) == NULL)
-                               break;
-               } else {
-                       if (fgets(line, LSIZE, stdin) == NULL)
-                               break;
-               }
-               if (*line == '.' && line[1] == '\n' && isatty(fileno(stdin)))
-                       break;
-               if (isfrom(line))
-                       putc('>', tmpf);
-               fputs(line, tmpf);
-               flgf = 0;
-       }
-       putc('\n', tmpf);
-       nlet = 1;
-       let[0].adr = 0;
-       let[1].adr = ftell(tmpf);
-       if (flgf)
-               return;
-       while (--argc > 0)
-               if (!sendmail(0, *++argv, truename))
-                       error++;
-       if (error && safefile(dead)) {
-               setuid(getuid());
-               malf = fopen(dead, "w");
-               if (malf == NULL) {
-                       printf("mail: cannot open %s\n", dead);
-                       fclose(tmpf);
+       if (lstat(path, &sb)) {
+               if ((mbfd = open(path,
+                   O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR)) < 0)
+                       mbfd = open(path, O_APPEND|O_WRONLY, 0);
+               else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
+                       e_to_sys(errno);
+                       warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name);
                        return;
                }
                        return;
                }
-               copylet(0, malf, ZAP);
-               fclose(malf);
-               printf("Mail saved in %s\n", dead);
-       }
-       fclose(tmpf);
-}
+       } else if (sb.st_nlink != 1 || S_ISLNK(sb.st_mode)) {
+               e_to_sys(errno);
+               warn("%s: linked file", path);
+               return;
+       } else
+               mbfd = open(path, O_APPEND|O_WRONLY, 0);
 
 
-sendrmt(n, name)
-char *name;
-{
-       FILE *rmf, *popen();
-       register char *p;
-       char rsys[64], cmd[64];
-       register pid;
-       int sts;
-
-#ifdef notdef
-       if (any('^', name)) {
-               while (p = index(name, '^'))
-                       *p = '!';
-               if (strncmp(name, "researc", 7)) {
-                       strcpy(rsys, "research");
-                       if (*name != '!')
-                               --name;
-                       goto skip;
-               }
+       if (mbfd == -1) {
+               e_to_sys(errno);
+               warn("%s: %s", path, strerror(errno));
+               return;
        }
        }
-#endif
-       for (p=rsys; *name!='!'; *p++ = *name++)
-               if (*name=='\0')
-                       return(0);      /* local address, no '!' */
-       *p = '\0';
-       if (name[1]=='\0') {
-               printf("null name\n");
-               return(0);
+
+       /* Wait until we can get a lock on the file. */
+       if (flock(mbfd, LOCK_EX)) {
+               e_to_sys(errno);
+               warn("%s: %s", path, strerror(errno));
+               goto err1;
        }
        }
-skip:
-       if ((pid = fork()) == -1) {
-               fprintf(stderr, "mail: can't create proc for remote\n");
-               return(0);
+
+       /* Get the starting offset of the new message for biff. */
+       curoff = lseek(mbfd, (off_t)0, SEEK_END);
+       (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%qd\n", name, curoff);
+
+       /* Copy the message into the file. */
+       if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
+               e_to_sys(errno);
+               warn("temporary file: %s", strerror(errno));
+               goto err1;
        }
        }
-       if (pid) {
-               while (wait(&sts) != pid) {
-                       if (wait(&sts)==-1)
-                               return(0);
-               }
-               return(!sts);
+       while ((nr = read(fd, buf, sizeof(buf))) > 0)
+               for (off = 0; off < nr; nr -= nw, off += nw)
+                       if ((nw = write(mbfd, buf + off, nr)) < 0) {
+                               e_to_sys(errno);
+                               warn("%s: %s", path, strerror(errno));
+                               goto err2;;
+                       }
+       if (nr < 0) {
+               e_to_sys(errno);
+               warn("temporary file: %s", strerror(errno));
+               goto err2;;
        }
        }
-       setuid(getuid());
-       if (any('!', name+1))
-               sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1);
-       else
-               sprintf(cmd, "uux - %s!rmail %s", rsys, name+1);
-       if ((rmf=popen(cmd, "w")) == NULL)
-               exit(1);
-       copylet(n, rmf, REMOTE);
-       exit(pclose(rmf) != 0);
-}
 
 
-usage()
-{
+       /* Flush to disk, don't wait for update. */
+       if (fsync(mbfd)) {
+               e_to_sys(errno);
+               warn("%s: %s", path, strerror(errno));
+err2:          (void)ftruncate(mbfd, curoff);
+err1:          (void)close(mbfd);
+               return;
+       }
+               
+       /* Close and check -- NFS doesn't write until the close. */
+       if (close(mbfd)) {
+               e_to_sys(errno);
+               warn("%s: %s", path, strerror(errno));
+               return;
+       }
 
 
-       fprintf(stderr, "Usage: mail [ -f ] people . . .\n");
-       error = EX_USAGE;
-       done();
+       notifybiff(biffmsg);
 }
 
 }
 
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-
+void
 notifybiff(msg)
        char *msg;
 {
        static struct sockaddr_in addr;
        static int f = -1;
 notifybiff(msg)
        char *msg;
 {
        static struct sockaddr_in addr;
        static int f = -1;
+       struct hostent *hp;
+       struct servent *sp;
+       int len;
 
 
-       if (addr.sin_family == 0) {
-               struct hostent *hp = gethostbyname("localhost");
-               struct servent *sp = getservbyname("biff", "udp");
-
-               if (hp && sp) {
-                       addr.sin_family = hp->h_addrtype;
-                       bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
-                       addr.sin_port = sp->s_port;
+       if (!addr.sin_family) {
+               /* Be silent if biff service not available. */
+               if (!(sp = getservbyname("biff", "udp")))
+                       return;
+               if (!(hp = gethostbyname("localhost"))) {
+                       warn("localhost: %s", strerror(errno));
+                       return;
                }
                }
+               addr.sin_family = hp->h_addrtype;
+               memmove(&addr.sin_addr, hp->h_addr, hp->h_length);
+               addr.sin_port = sp->s_port;
        }
        }
-       if (addr.sin_family) {
-               if (f < 0)
-                       f = socket(AF_INET, SOCK_DGRAM, 0);
-               if (f >= 0)
-                       sendto(f, msg, strlen(msg)+1, 0, &addr, sizeof (addr));
+       if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+               warn("socket: %s", strerror(errno));
+               return;
        }
        }
+       len = strlen(msg) + 1;
+       if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
+           != len)
+               warn("sendto biff: %s", strerror(errno));
 }
 
 }
 
-sendmail(n, name, fromaddr)
-       int n;
-       char *name, *fromaddr;
+void
+usage()
 {
 {
-       char file[256];
-       int mask, fd;
-       struct passwd *pw;
-#ifdef notdef
-       struct stat statb;
-#endif
-       char buf[128];
-
-       if (*name=='!')
-               name++;
-       if (any('!', name))
-               return (sendrmt(n, name));
-       if ((pw = getpwnam(name)) == NULL) {
-               printf("mail: can't send to %s\n", name);
-               return(0);
-       }
-       cat(file, maildir, name);
-#ifdef notdef
-       if (stat(file, &statb) >= 0 && (statb.st_mode & S_IFMT) == S_IFDIR) {
-               strcat(file, "/");
-               strcat(file, name);
-       }
-#endif
-       if (!safefile(file))
-               return(0);
-       fd = open(file, O_WRONLY | O_CREAT, MAILMODE);
-       if (fd >= 0) {
-               flock(fd, LOCK_EX);
-               malf = fdopen(fd, "a");
-       }
-       if (fd < 0 || malf == NULL) {
-               close(fd);
-               printf("mail: %s: cannot append\n", file);
-               return(0);
-       }
-       fchown(fd, pw->pw_uid, pw->pw_gid);
-       sprintf(buf, "%s@%d\n", name, ftell(malf)); 
-       copylet(n, malf, ORDINARY);
-       fclose(malf);
-       notifybiff(buf);
-       return(1);
+       eval = EX_USAGE;
+       err("usage: mail.local [-f from] user ...");
 }
 
 }
 
-delex(i)
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+       const char *fmt;
+       va_dcl
+#endif
 {
 {
-       if (i != SIGINT) {
-               setsig(i, SIG_DFL);
-               sigsetmask(sigblock(0) &~ sigmask(i));
-       }
-       putc('\n', stderr);
-       if (delflg)
-               longjmp(sjbuf, 1);
-       if (error == 0)
-               error = i;
-       done();
-}
+       va_list ap;
 
 
-done()
-{
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       vwarn(fmt, ap);
+       va_end(ap);
 
 
-       unlink(lettmp);
-       exit(error);
+       exit(eval);
 }
 
 }
 
-cat(to, from1, from2)
-       char *to, *from1, *from2;
+void
+#if __STDC__
+warn(const char *fmt, ...)
+#else
+warn(fmt, va_alist)
+       const char *fmt;
+       va_dcl
+#endif
 {
 {
-       register char *cp, *dp;
+       va_list ap;
 
 
-       cp = to;
-       for (dp = from1; *cp = *dp++; cp++)
-               ;
-       for (dp = from2; *cp++ = *dp++; )
-               ;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       vwarn(fmt, ap);
+       va_end(ap);
 }
 
 }
 
-/* copy p... into s, update p */
-char *
-getarg(s, p)
-       register char *s, *p;
+void
+vwarn(fmt, ap)
+       const char *fmt;
+       _BSD_VA_LIST_ ap;
 {
 {
-       while (*p == ' ' || *p == '\t')
-               p++;
-       if (*p == '\n' || *p == '\0')
-               return(NULL);
-       while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
-               *s++ = *p++;
-       *s = '\0';
-       return(p);
-}
+       /*
+        * Log the message to stderr.
+        *
+        * Don't use LOG_PERROR as an openlog() flag to do this,
+        * it's not portable enough.
+        */
+       if (eval != EX_USAGE)
+               (void)fprintf(stderr, "mail.local: ");
+       (void)vfprintf(stderr, fmt, ap);
+       (void)fprintf(stderr, "\n");
 
 
-safefile(f)
-       char *f;
-{
-       struct stat statb;
-
-       if (lstat(f, &statb) < 0)
-               return (1);
-       if (statb.st_nlink != 1 || (statb.st_mode & S_IFMT) == S_IFLNK) {
-               fprintf(stderr,
-                   "mail: %s has more than one link or is a symbolic link\n",
-                   f);
-               return (0);
-       }
-       return (1);
+       /* Log the message to syslog. */
+       vsyslog(LOG_ERR, fmt, ap);
 }
 
 }
 
-panic(msg, a1, a2, a3)
-       char *msg;
+/*
+ * e_to_sys --
+ *     Guess which errno's are temporary.  Gag me.
+ */
+void
+e_to_sys(num)
+       int num;
 {
 {
+       /* Temporary failures override hard errors. */
+       if (eval == EX_TEMPFAIL)
+               return;
 
 
-       fprintf(stderr, "mail: ");
-       fprintf(stderr, msg, a1, a2, a3);
-       fprintf(stderr, "\n");
-       done();
+       switch(num) {           /* Hopefully temporary errors. */
+#ifdef EAGAIN
+       case EAGAIN:            /* Resource temporarily unavailable */
+#endif
+#ifdef EDQUOT
+       case EDQUOT:            /* Disc quota exceeded */
+#endif
+#ifdef EBUSY
+       case EBUSY:             /* Device busy */
+#endif
+#ifdef EPROCLIM
+       case EPROCLIM:          /* Too many processes */
+#endif
+#ifdef EUSERS
+       case EUSERS:            /* Too many users */
+#endif
+#ifdef ECONNABORTED
+       case ECONNABORTED:      /* Software caused connection abort */
+#endif
+#ifdef ECONNREFUSED
+       case ECONNREFUSED:      /* Connection refused */
+#endif
+#ifdef ECONNRESET
+       case ECONNRESET:        /* Connection reset by peer */
+#endif
+#ifdef EDEADLK
+       case EDEADLK:           /* Resource deadlock avoided */
+#endif
+#ifdef EFBIG
+       case EFBIG:             /* File too large */
+#endif
+#ifdef EHOSTDOWN
+       case EHOSTDOWN:         /* Host is down */
+#endif
+#ifdef EHOSTUNREACH
+       case EHOSTUNREACH:      /* No route to host */
+#endif
+#ifdef EMFILE
+       case EMFILE:            /* Too many open files */
+#endif
+#ifdef ENETDOWN
+       case ENETDOWN:          /* Network is down */
+#endif
+#ifdef ENETRESET
+       case ENETRESET:         /* Network dropped connection on reset */
+#endif
+#ifdef ENETUNREACH
+       case ENETUNREACH:       /* Network is unreachable */
+#endif
+#ifdef ENFILE
+       case ENFILE:            /* Too many open files in system */
+#endif
+#ifdef ENOBUFS
+       case ENOBUFS:           /* No buffer space available */
+#endif
+#ifdef ENOMEM
+       case ENOMEM:            /* Cannot allocate memory */
+#endif
+#ifdef ENOSPC
+       case ENOSPC:            /* No space left on device */
+#endif
+#ifdef EROFS
+       case EROFS:             /* Read-only file system */
+#endif
+#ifdef ESTALE
+       case ESTALE:            /* Stale NFS file handle */
+#endif
+#ifdef ETIMEDOUT
+       case ETIMEDOUT:         /* Connection timed out */
+#endif
+#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
+       case EWOULDBLOCK:       /* Operation would block. */
+#endif
+               eval = EX_TEMPFAIL;
+               break;
+       default:
+               eval = EX_UNAVAILABLE;
+               break;
+       }
 }
 }