install approved copyright notice
[unix-history] / usr / src / usr.sbin / sendmail / src / savemail.c
index 06aa326..bf0ccdb 100644 (file)
@@ -1,8 +1,28 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)savemail.c 5.9 (Berkeley) %G%";
+#endif /* not lint */
+
 # include <pwd.h>
 # include "sendmail.h"
 
 # include <pwd.h>
 # include "sendmail.h"
 
-SCCSID(@(#)savemail.c  3.54            %G%);
-
 /*
 **  SAVEMAIL -- Save mail on error
 **
 /*
 **  SAVEMAIL -- Save mail on error
 **
@@ -23,11 +43,24 @@ SCCSID(@(#)savemail.c       3.54            %G%);
 **             directory.
 */
 
 **             directory.
 */
 
+/* defines for state machine */
+# define ESM_REPORT    0       /* report to sender's terminal */
+# define ESM_MAIL      1       /* mail back to sender */
+# define ESM_QUIET     2       /* messages have already been returned */
+# define ESM_DEADLETTER        3       /* save in ~/dead.letter */
+# define ESM_POSTMASTER        4       /* return to postmaster */
+# define ESM_USRTMP    5       /* save in /usr/tmp/dead.letter */
+# define ESM_PANIC     6       /* leave the locked queue/transcript files */
+# define ESM_DONE      7       /* the message is successfully delivered */
+
+
 savemail(e)
        register ENVELOPE *e;
 {
        register struct passwd *pw;
 savemail(e)
        register ENVELOPE *e;
 {
        register struct passwd *pw;
-       register FILE *xfile;
+       register FILE *fp;
+       int state;
+       auto ADDRESS *q;
        char buf[MAXLINE+1];
        extern struct passwd *getpwnam();
        register char *p;
        char buf[MAXLINE+1];
        extern struct passwd *getpwnam();
        register char *p;
@@ -36,7 +69,7 @@ savemail(e)
 
 # ifdef DEBUG
        if (tTd(6, 1))
 
 # ifdef DEBUG
        if (tTd(6, 1))
-               printf("\nsavemail\n");
+               printf("\nsavemail, ErrorMode = %c\n", ErrorMode);
 # endif DEBUG
 
        if (bitset(EF_RESPONSE, e->e_flags))
 # endif DEBUG
 
        if (bitset(EF_RESPONSE, e->e_flags))
@@ -67,49 +100,92 @@ savemail(e)
        e->e_to = NULL;
 
        /*
        e->e_to = NULL;
 
        /*
-       **  If called from Eric Schmidt's network, do special mailback.
-       **      Fundamentally, this is the mailback case except that
-       **      it returns an OK exit status (assuming the return
-       **      worked).
-       **  Also, if the from address is not local, mail it back.
+       **  Basic state machine.
+       **
+       **      This machine runs through the following states:
+       **
+       **      ESM_QUIET       Errors have already been printed iff the
+       **                      sender is local.
+       **      ESM_REPORT      Report directly to the sender's terminal.
+       **      ESM_MAIL        Mail response to the sender.
+       **      ESM_DEADLETTER  Save response in ~/dead.letter.
+       **      ESM_POSTMASTER  Mail response to the postmaster.
+       **      ESM_PANIC       Save response anywhere possible.
        */
 
        */
 
-       if (ErrorMode == EM_BERKNET)
+       /* determine starting state */
+       switch (ErrorMode)
        {
        {
+         case EM_WRITE:
+               state = ESM_REPORT;
+               break;
+
+         case EM_BERKNET:
+               /* mail back, but return o.k. exit status */
                ExitStat = EX_OK;
                ExitStat = EX_OK;
-               ErrorMode = EM_MAIL;
-       }
-       if (!bitset(M_LOCAL, CurEnv->e_returnto->q_mailer->m_flags))
-               ErrorMode = EM_MAIL;
 
 
-       /*
-       **  If writing back, do it.
-       **      If the user is still logged in on the same terminal,
-       **      then write the error messages back to hir (sic).
-       **      If not, mail back instead.
-       */
+               /* fall through.... */
+
+         case EM_MAIL:
+               state = ESM_MAIL;
+               break;
 
 
-       if (ErrorMode == EM_WRITE)
+         case EM_PRINT:
+         case '\0':
+               state = ESM_QUIET;
+               break;
+
+         case EM_QUIET:
+               /* no need to return anything at all */
+               return;
+
+         default:
+               syserr("savemail: ErrorMode x%x\n");
+               state = ESM_MAIL;
+               break;
+       }
+
+       while (state != ESM_DONE)
        {
        {
-               p = ttypath();
-               if (p == NULL || freopen(p, "w", stdout) == NULL)
-               {
-                       ErrorMode = EM_MAIL;
-                       errno = 0;
-               }
-               else
+# ifdef DEBUG
+               if (tTd(6, 5))
+                       printf("  state %d\n", state);
+# endif DEBUG
+
+               switch (state)
                {
                {
-                       expand("$n", buf, &buf[sizeof buf - 1], e);
+                 case ESM_QUIET:
+                       if (e->e_from.q_mailer == LocalMailer)
+                               state = ESM_DEADLETTER;
+                       else
+                               state = ESM_MAIL;
+                       break;
+
+                 case ESM_REPORT:
+
+                       /*
+                       **  If the user is still logged in on the same terminal,
+                       **  then write the error messages back to hir (sic).
+                       */
+
+                       p = ttypath();
+                       if (p == NULL || freopen(p, "w", stdout) == NULL)
+                       {
+                               state = ESM_MAIL;
+                               break;
+                       }
+
+                       expand("\001n", buf, &buf[sizeof buf - 1], e);
                        printf("\r\nMessage from %s...\r\n", buf);
                        printf("Errors occurred while sending mail.\r\n");
                        if (e->e_xfp != NULL)
                        {
                                (void) fflush(e->e_xfp);
                        printf("\r\nMessage from %s...\r\n", buf);
                        printf("Errors occurred while sending mail.\r\n");
                        if (e->e_xfp != NULL)
                        {
                                (void) fflush(e->e_xfp);
-                               xfile = fopen(queuename(e, 'x'), "r");
+                               fp = fopen(queuename(e, 'x'), "r");
                        }
                        else
                        }
                        else
-                               xfile = NULL;
-                       if (xfile == NULL)
+                               fp = NULL;
+                       if (fp == NULL)
                        {
                                syserr("Cannot open %s", queuename(e, 'x'));
                                printf("Transcript of session is unavailable.\r\n");
                        {
                                syserr("Cannot open %s", queuename(e, 'x'));
                                printf("Transcript of session is unavailable.\r\n");
@@ -117,83 +193,160 @@ savemail(e)
                        else
                        {
                                printf("Transcript follows:\r\n");
                        else
                        {
                                printf("Transcript follows:\r\n");
-                               while (fgets(buf, sizeof buf, xfile) != NULL &&
+                               while (fgets(buf, sizeof buf, fp) != NULL &&
                                       !ferror(stdout))
                                        fputs(buf, stdout);
                                       !ferror(stdout))
                                        fputs(buf, stdout);
-                               (void) fclose(xfile);
+                               (void) fclose(fp);
                        }
                        }
+                       printf("Original message will be saved in dead.letter.\r\n");
                        if (ferror(stdout))
                                (void) syserr("savemail: stdout: write err");
                        if (ferror(stdout))
                                (void) syserr("savemail: stdout: write err");
-               }
-       }
+                       state = ESM_DEADLETTER;
+                       break;
+
+                 case ESM_MAIL:
+                 case ESM_POSTMASTER:
+                       /*
+                       **  If mailing back, do it.
+                       **      Throw away all further output.  Don't alias,
+                       **      since this could cause loops, e.g., if joe
+                       **      mails to joe@x, and for some reason the network
+                       **      for @x is down, then the response gets sent to
+                       **      joe@x, which gives a response, etc.  Also force
+                       **      the mail to be delivered even if a version of
+                       **      it has already been sent to the sender.
+                       */
+
+                       if (state == ESM_MAIL)
+                       {
+                               if (e->e_errorqueue == NULL)
+                                       sendtolist(e->e_from.q_paddr,
+                                               (ADDRESS *) NULL,
+                                               &e->e_errorqueue);
+
+                               /* deliver a cc: to the postmaster if desired */
+                               if (PostMasterCopy != NULL)
+                                       sendtolist(PostMasterCopy,
+                                               (ADDRESS *) NULL,
+                                               &e->e_errorqueue);
+                               q = e->e_errorqueue;
+                       }
+                       else
+                       {
+                               if (parseaddr("postmaster", q, 0, '\0') == NULL)
+                               {
+                                       syserr("cannot parse postmaster!");
+                                       ExitStat = EX_SOFTWARE;
+                                       state = ESM_USRTMP;
+                                       break;
+                               }
+                       }
+                       if (returntosender(e->e_message != NULL ? e->e_message :
+                                          "Unable to deliver mail",
+                                          q, TRUE) == 0)
+                       {
+                               state = ESM_DONE;
+                               break;
+                       }
 
 
-       /*
-       **  If mailing back, do it.
-       **      Throw away all further output.  Don't do aliases, since
-       **      this could cause loops, e.g., if joe mails to x:joe,
-       **      and for some reason the network for x: is down, then
-       **      the response gets sent to x:joe, which gives a
-       **      response, etc.  Also force the mail to be delivered
-       **      even if a version of it has already been sent to the
-       **      sender.
-       */
+                       state = state == ESM_MAIL ? ESM_POSTMASTER : ESM_USRTMP;
+                       break;
+
+                 case ESM_DEADLETTER:
+                       /*
+                       **  Save the message in dead.letter.
+                       **      If we weren't mailing back, and the user is
+                       **      local, we should save the message in
+                       **      ~/dead.letter so that the poor person doesn't
+                       **      have to type it over again -- and we all know
+                       **      what poor typists UNIX users are.
+                       */
+
+                       p = NULL;
+                       if (e->e_from.q_mailer == LocalMailer)
+                       {
+                               if (e->e_from.q_home != NULL)
+                                       p = e->e_from.q_home;
+                               else if ((pw = getpwnam(e->e_from.q_user)) != NULL)
+                                       p = pw->pw_dir;
+                       }
+                       if (p == NULL)
+                       {
+                               syserr("Can't return mail to %s", e->e_from.q_paddr);
+                               state = ESM_MAIL;
+                               break;
+                       }
+                       if (e->e_dfp != NULL)
+                       {
+                               auto ADDRESS *q;
+                               bool oldverb = Verbose;
+
+                               /* we have a home directory; open dead.letter */
+                               define('z', p, e);
+                               expand("\001z/dead.letter", buf, &buf[sizeof buf - 1], e);
+                               Verbose = TRUE;
+                               message(Arpa_Info, "Saving message in %s", buf);
+                               Verbose = oldverb;
+                               e->e_to = buf;
+                               q = NULL;
+                               sendtolist(buf, (ADDRESS *) NULL, &q);
+                               if (deliver(e, q) == 0)
+                                       state = ESM_DONE;
+                               else
+                                       state = ESM_MAIL;
+                       }
+                       else
+                       {
+                               /* no data file -- try mailing back */
+                               state = ESM_MAIL;
+                       }
+                       break;
 
 
-       if (ErrorMode == EM_MAIL)
-       {
-               if (returntosender("Unable to deliver mail", CurEnv->e_returnto, TRUE) == 0)
-                       return;
-       }
+                 case ESM_USRTMP:
+                       /*
+                       **  Log the mail in /usr/tmp/dead.letter.
+                       */
 
 
-       /*
-       **  Save the message in dead.letter.
-       **      If we weren't mailing back, and the user is local, we
-       **      should save the message in dead.letter so that the
-       **      poor person doesn't have to type it over again --
-       **      and we all know what poor typists programmers are.
-       */
+                       fp = dfopen("/usr/tmp/dead.letter", "a");
+                       if (fp == NULL)
+                       {
+                               state = ESM_PANIC;
+                               break;
+                       }
 
 
-       p = NULL;
-       if (CurEnv->e_returnto->q_mailer == LocalMailer)
-       {
-               if (CurEnv->e_returnto->q_home != NULL)
-                       p = CurEnv->e_returnto->q_home;
-               else if ((pw = getpwnam(CurEnv->e_returnto->q_user)) != NULL)
-                       p = pw->pw_dir;
-       }
-       if (p == NULL)
-       {
-               syserr("Can't return mail to %s", CurEnv->e_returnto->q_paddr);
-# ifdef DEBUG
-               p = "/usr/tmp";
-# endif
-       }
-       if (p != NULL && e->e_dfp != NULL)
-       {
-               auto ADDRESS *q;
-               bool oldverb = Verbose;
-
-               /* we have a home directory; open dead.letter */
-               define('z', p, e);
-               expand("$z/dead.letter", buf, &buf[sizeof buf - 1], e);
-               Verbose = TRUE;
-               message(Arpa_Info, "Saving message in %s", buf);
-               Verbose = oldverb;
-               e->e_to = buf;
-               q = NULL;
-               sendtolist(buf, (ADDRESS *) NULL, &q);
-               (void) deliver(e, q);
-       }
+                       putfromline(fp, ProgMailer);
+                       (*e->e_puthdr)(fp, ProgMailer, e);
+                       putline("\n", fp, ProgMailer);
+                       (*e->e_putbody)(fp, ProgMailer, e);
+                       putline("\n", fp, ProgMailer);
+                       (void) fflush(fp);
+                       state = ferror(fp) ? ESM_PANIC : ESM_DONE;
+                       (void) fclose(fp);
+                       break;
+
+                 default:
+                       syserr("savemail: unknown state %d", state);
+
+                       /* fall through ... */
 
 
-       /* add terminator to writeback message */
-       if (ErrorMode == EM_WRITE)
-               printf("-----\r\n");
+                 case ESM_PANIC:
+                       syserr("savemail: HELP!!!!");
+# ifdef LOG
+                       if (LogLevel >= 1)
+                               syslog(LOG_ALERT, "savemail: HELP!!!!");
+# endif LOG
+
+                       /* leave the locked queue & transcript files around */
+                       exit(EX_SOFTWARE);
+               }
+       }
 }
 \f/*
 **  RETURNTOSENDER -- return a message to the sender with an error.
 **
 **     Parameters:
 **             msg -- the explanatory message.
 }
 \f/*
 **  RETURNTOSENDER -- return a message to the sender with an error.
 **
 **     Parameters:
 **             msg -- the explanatory message.
-**             returnto -- the queue of people to send the message to.
+**             returnq -- the queue of people to send the message to.
 **             sendbody -- if TRUE, also send back the body of the
 **                     message; otherwise just send the header.
 **
 **             sendbody -- if TRUE, also send back the body of the
 **                     message; otherwise just send the header.
 **
@@ -210,9 +363,9 @@ static bool SendBody;
 
 #define MAXRETURNS     6       /* max depth of returning messages */
 
 
 #define MAXRETURNS     6       /* max depth of returning messages */
 
-returntosender(msg, returnto, sendbody)
+returntosender(msg, returnq, sendbody)
        char *msg;
        char *msg;
-       ADDRESS *returnto;
+       ADDRESS *returnq;
        bool sendbody;
 {
        char buf[MAXNAME];
        bool sendbody;
 {
        char buf[MAXNAME];
@@ -228,52 +381,57 @@ returntosender(msg, returnto, sendbody)
        {
                printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n",
                       msg, returndepth, CurEnv);
        {
                printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n",
                       msg, returndepth, CurEnv);
-               printf("\treturnto=");
-               printaddr(returnto, TRUE);
+               printf("\treturnq=");
+               printaddr(returnq, TRUE);
        }
 # endif DEBUG
 
        if (++returndepth >= MAXRETURNS)
        {
                if (returndepth != MAXRETURNS)
        }
 # endif DEBUG
 
        if (++returndepth >= MAXRETURNS)
        {
                if (returndepth != MAXRETURNS)
-                       syserr("returntosender: infinite recursion on %s", returnto->q_paddr);
+                       syserr("returntosender: infinite recursion on %s", returnq->q_paddr);
                /* don't "unrecurse" and fake a clean exit */
                /* returndepth--; */
                return (0);
        }
 
        SendBody = sendbody;
                /* don't "unrecurse" and fake a clean exit */
                /* returndepth--; */
                return (0);
        }
 
        SendBody = sendbody;
-       define('g', "$f", CurEnv);
+       define('g', "\001f", CurEnv);
        ee = newenvelope(&errenvelope);
        ee = newenvelope(&errenvelope);
+       define('a', "\001b", ee);
        ee->e_puthdr = putheader;
        ee->e_putbody = errbody;
        ee->e_flags |= EF_RESPONSE;
        ee->e_puthdr = putheader;
        ee->e_putbody = errbody;
        ee->e_flags |= EF_RESPONSE;
-       ee->e_sendqueue = returnto;
+       ee->e_sendqueue = returnq;
        openxscript(ee);
        openxscript(ee);
-       for (q = returnto; q != NULL; q = q->q_next)
+       for (q = returnq; q != NULL; q = q->q_next)
        {
                if (q->q_alias == NULL)
                        addheader("to", q->q_paddr, ee);
        }
        {
                if (q->q_alias == NULL)
                        addheader("to", q->q_paddr, ee);
        }
-       addheader("subject", msg, ee);
+
+       (void) sprintf(buf, "Returned mail: %s", msg);
+       addheader("subject", buf, ee);
 
        /* fake up an address header for the from person */
 
        /* fake up an address header for the from person */
-       expand("$n", buf, &buf[sizeof buf - 1], CurEnv);
-       if (parseaddr(buf, &ee->e_from, -1) == NULL)
+       expand("\001n", buf, &buf[sizeof buf - 1], CurEnv);
+       if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL)
        {
                syserr("Can't parse myself!");
                ExitStat = EX_SOFTWARE;
                returndepth--;
                return (-1);
        }
        {
                syserr("Can't parse myself!");
                ExitStat = EX_SOFTWARE;
                returndepth--;
                return (-1);
        }
+       loweraddr(&ee->e_from);
 
        /* push state into submessage */
        CurEnv = ee;
 
        /* push state into submessage */
        CurEnv = ee;
-       define('f', "$n", ee);
+       define('f', "\001n", ee);
        define('x', "Mail Delivery Subsystem", ee);
        define('x', "Mail Delivery Subsystem", ee);
+       eatheader(ee);
 
        /* actually deliver the error message */
 
        /* actually deliver the error message */
-       sendall(ee, SendMode);
+       sendall(ee, SM_DEFAULT);
 
        /* restore state */
        dropenvelope(ee);
 
        /* restore state */
        dropenvelope(ee);
@@ -290,11 +448,9 @@ returntosender(msg, returnto, sendbody)
 **     original offending message.
 **
 **     Parameters:
 **     original offending message.
 **
 **     Parameters:
-**             xfile -- the transcript file.
 **             fp -- the output file.
 **             fp -- the output file.
-**             xdot -- if set, use the SMTP hidden dot algorithm.
+**             m -- the mailer to output to.
 **             e -- the envelope we are working in.
 **             e -- the envelope we are working in.
-**             crlf -- set if we want CRLF's at the end of lines.
 **
 **     Returns:
 **             none
 **
 **     Returns:
 **             none
@@ -303,16 +459,13 @@ returntosender(msg, returnto, sendbody)
 **             Outputs the body of an error message.
 */
 
 **             Outputs the body of an error message.
 */
 
-errbody(fp, m, xdot, e, crlf)
+errbody(fp, m, e)
        register FILE *fp;
        register struct mailer *m;
        register FILE *fp;
        register struct mailer *m;
-       bool xdot;
        register ENVELOPE *e;
        register ENVELOPE *e;
-       bool crlf;
 {
        register FILE *xfile;
        char buf[MAXLINE];
 {
        register FILE *xfile;
        char buf[MAXLINE];
-       bool fullsmtp = bitset(M_FULLSMTP, m->m_flags);
        char *p;
 
        /*
        char *p;
 
        /*
@@ -332,7 +485,7 @@ errbody(fp, m, xdot, e, crlf)
                if (e->e_xfp != NULL)
                        (void) fflush(e->e_xfp);
                while (fgets(buf, sizeof buf, xfile) != NULL)
                if (e->e_xfp != NULL)
                        (void) fflush(e->e_xfp);
                while (fgets(buf, sizeof buf, xfile) != NULL)
-                       putline(buf, fp, crlf, fullsmtp);
+                       putline(buf, fp, m);
                (void) fclose(xfile);
        }
        errno = 0;
                (void) fclose(xfile);
        }
        errno = 0;
@@ -347,21 +500,27 @@ errbody(fp, m, xdot, e, crlf)
        {
                if (SendBody)
                {
        {
                if (SendBody)
                {
-                       fprintf(fp, "\n   ----- Unsent message follows -----\n");
+                       putline("\n", fp, m);
+                       putline("   ----- Unsent message follows -----\n", fp, m);
                        (void) fflush(fp);
                        (void) fflush(fp);
-                       putheader(fp, m, e->e_parent, crlf);
-                       fprintf(fp, "\n");
-                       putbody(fp, m, xdot, e->e_parent, crlf);
+                       putheader(fp, m, e->e_parent);
+                       putline("\n", fp, m);
+                       putbody(fp, m, e->e_parent);
                }
                else
                {
                }
                else
                {
-                       fprintf(fp, "\n  ----- Message header follows -----\n");
+                       putline("\n", fp, m);
+                       putline("  ----- Message header follows -----\n", fp, m);
                        (void) fflush(fp);
                        (void) fflush(fp);
-                       putheader(fp, m, e->e_parent, crlf);
+                       putheader(fp, m, e->e_parent);
                }
        }
        else
                }
        }
        else
-               fprintf(fp, "\n  ----- No message was collected -----\n\n");
+       {
+               putline("\n", fp, m);
+               putline("  ----- No message was collected -----\n", fp, m);
+               putline("\n", fp, m);
+       }
 
        /*
        **  Cleanup and exit
 
        /*
        **  Cleanup and exit