home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.sbin / sendmail / src / savemail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-20  |  12.9 KB  |  542 lines

  1. /*
  2.  * Copyright (c) 1983 Eric P. Allman
  3.  * Copyright (c) 1988 Regents of the University of California.
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  * 1. Redistributions of source code must retain the above copyright
  10.  *    notice, this list of conditions and the following disclaimer.
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in the
  13.  *    documentation and/or other materials provided with the distribution.
  14.  * 3. All advertising materials mentioning features or use of this software
  15.  *    must display the following acknowledgement:
  16.  *    This product includes software developed by the University of
  17.  *    California, Berkeley and its contributors.
  18.  * 4. Neither the name of the University nor the names of its contributors
  19.  *    may be used to endorse or promote products derived from this software
  20.  *    without specific prior written permission.
  21.  *
  22.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  23.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  26.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  27.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  28.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  31.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32.  * SUCH DAMAGE.
  33.  */
  34.  
  35. #ifndef lint
  36. static char sccsid[] = "@(#)savemail.c    5.14 (Berkeley) 8/29/90";
  37. #endif /* not lint */
  38.  
  39. # include <sys/types.h>
  40. # include <pwd.h>
  41. # include "sendmail.h"
  42.  
  43. /*
  44. **  SAVEMAIL -- Save mail on error
  45. **
  46. **    If mailing back errors, mail it back to the originator
  47. **    together with an error message; otherwise, just put it in
  48. **    dead.letter in the user's home directory (if he exists on
  49. **    this machine).
  50. **
  51. **    Parameters:
  52. **        e -- the envelope containing the message in error.
  53. **
  54. **    Returns:
  55. **        none
  56. **
  57. **    Side Effects:
  58. **        Saves the letter, by writing or mailing it back to the
  59. **        sender, or by putting it in dead.letter in her home
  60. **        directory.
  61. */
  62.  
  63. /* defines for state machine */
  64. # define ESM_REPORT    0    /* report to sender's terminal */
  65. # define ESM_MAIL    1    /* mail back to sender */
  66. # define ESM_QUIET    2    /* messages have already been returned */
  67. # define ESM_DEADLETTER    3    /* save in ~/dead.letter */
  68. # define ESM_POSTMASTER    4    /* return to postmaster */
  69. # define ESM_USRTMP    5    /* save in /usr/tmp/dead.letter */
  70. # define ESM_PANIC    6    /* leave the locked queue/transcript files */
  71. # define ESM_DONE    7    /* the message is successfully delivered */
  72.  
  73.  
  74. savemail(e)
  75.     register ENVELOPE *e;
  76. {
  77.     register struct passwd *pw;
  78.     register FILE *fp;
  79.     int state;
  80.     auto ADDRESS *q;
  81.     char buf[MAXLINE+1];
  82.     extern struct passwd *getpwnam();
  83.     register char *p;
  84.     extern char *ttypath();
  85.     typedef int (*fnptr)();
  86.  
  87.     if (tTd(6, 1))
  88.         printf("\nsavemail, ErrorMode = %c\n", ErrorMode);
  89.  
  90.     if (bitset(EF_RESPONSE, e->e_flags))
  91.         return;
  92.     if (e->e_class < 0)
  93.     {
  94.         message(Arpa_Info, "Dumping junk mail");
  95.         return;
  96.     }
  97.     ForceMail = TRUE;
  98.     e->e_flags &= ~EF_FATALERRS;
  99.  
  100.     /*
  101.     **  In the unhappy event we don't know who to return the mail
  102.     **  to, make someone up.
  103.     */
  104.  
  105.     if (e->e_from.q_paddr == NULL)
  106.     {
  107.         if (parseaddr("root", &e->e_from, 0, '\0') == NULL)
  108.         {
  109.             syserr("Cannot parse root!");
  110.             ExitStat = EX_SOFTWARE;
  111.             finis();
  112.         }
  113.     }
  114.     e->e_to = NULL;
  115.  
  116.     /*
  117.     **  Basic state machine.
  118.     **
  119.     **    This machine runs through the following states:
  120.     **
  121.     **    ESM_QUIET    Errors have already been printed iff the
  122.     **            sender is local.
  123.     **    ESM_REPORT    Report directly to the sender's terminal.
  124.     **    ESM_MAIL    Mail response to the sender.
  125.     **    ESM_DEADLETTER    Save response in ~/dead.letter.
  126.     **    ESM_POSTMASTER    Mail response to the postmaster.
  127.     **    ESM_PANIC    Save response anywhere possible.
  128.     */
  129.  
  130.     /* determine starting state */
  131.     switch (ErrorMode)
  132.     {
  133.       case EM_WRITE:
  134.         state = ESM_REPORT;
  135.         break;
  136.  
  137.       case EM_BERKNET:
  138.         /* mail back, but return o.k. exit status */
  139.         ExitStat = EX_OK;
  140.  
  141.         /* fall through.... */
  142.  
  143.       case EM_MAIL:
  144.         state = ESM_MAIL;
  145.         break;
  146.  
  147.       case EM_PRINT:
  148.       case '\0':
  149.         state = ESM_QUIET;
  150.         break;
  151.  
  152.       case EM_QUIET:
  153.         /* no need to return anything at all */
  154.         return;
  155.  
  156.       default:
  157.         syserr("savemail: ErrorMode x%x\n");
  158.         state = ESM_MAIL;
  159.         break;
  160.     }
  161.  
  162.     while (state != ESM_DONE)
  163.     {
  164.         if (tTd(6, 5))
  165.             printf("  state %d\n", state);
  166.  
  167.         switch (state)
  168.         {
  169.           case ESM_QUIET:
  170.             if (e->e_from.q_mailer == LocalMailer)
  171.                 state = ESM_DEADLETTER;
  172.             else
  173.                 state = ESM_MAIL;
  174.             break;
  175.  
  176.           case ESM_REPORT:
  177.  
  178.             /*
  179.             **  If the user is still logged in on the same terminal,
  180.             **  then write the error messages back to hir (sic).
  181.             */
  182.  
  183.             p = ttypath();
  184.             if (p == NULL || freopen(p, "w", stdout) == NULL)
  185.             {
  186.                 state = ESM_MAIL;
  187.                 break;
  188.             }
  189.  
  190.             expand("\001n", buf, &buf[sizeof buf - 1], e);
  191.             printf("\r\nMessage from %s...\r\n", buf);
  192.             printf("Errors occurred while sending mail.\r\n");
  193.             if (e->e_xfp != NULL)
  194.             {
  195.                 (void) fflush(e->e_xfp);
  196.                 fp = fopen(queuename(e, 'x'), "r");
  197.             }
  198.             else
  199.                 fp = NULL;
  200.             if (fp == NULL)
  201.             {
  202.                 syserr("Cannot open %s", queuename(e, 'x'));
  203.                 printf("Transcript of session is unavailable.\r\n");
  204.             }
  205.             else
  206.             {
  207.                 printf("Transcript follows:\r\n");
  208.                 while (fgets(buf, sizeof buf, fp) != NULL &&
  209.                        !ferror(stdout))
  210.                     fputs(buf, stdout);
  211.                 (void) fclose(fp);
  212.             }
  213.             printf("Original message will be saved in dead.letter.\r\n");
  214.             state = ESM_DEADLETTER;
  215.             break;
  216.  
  217.           case ESM_MAIL:
  218.           case ESM_POSTMASTER:
  219.             /*
  220.             **  If mailing back, do it.
  221.             **    Throw away all further output.  Don't alias,
  222.             **    since this could cause loops, e.g., if joe
  223.             **    mails to joe@x, and for some reason the network
  224.             **    for @x is down, then the response gets sent to
  225.             **    joe@x, which gives a response, etc.  Also force
  226.             **    the mail to be delivered even if a version of
  227.             **    it has already been sent to the sender.
  228.             */
  229.  
  230.             if (state == ESM_MAIL)
  231.             {
  232.                 if (e->e_errorqueue == NULL)
  233.                     sendtolist(e->e_from.q_paddr,
  234.                         (ADDRESS *) NULL,
  235.                         &e->e_errorqueue);
  236.  
  237.                 /* deliver a cc: to the postmaster if desired */
  238.                 if (PostMasterCopy != NULL)
  239.                     sendtolist(PostMasterCopy,
  240.                         (ADDRESS *) NULL,
  241.                         &e->e_errorqueue);
  242.                 q = e->e_errorqueue;
  243.             }
  244.             else
  245.             {
  246.                 if (parseaddr("postmaster", q, 0, '\0') == NULL)
  247.                 {
  248.                     syserr("cannot parse postmaster!");
  249.                     ExitStat = EX_SOFTWARE;
  250.                     state = ESM_USRTMP;
  251.                     break;
  252.                 }
  253.             }
  254.             if (returntosender(e->e_message != NULL ? e->e_message :
  255.                        "Unable to deliver mail",
  256.                        q, TRUE) == 0)
  257.             {
  258.                 state = ESM_DONE;
  259.                 break;
  260.             }
  261.  
  262.             state = state == ESM_MAIL ? ESM_POSTMASTER : ESM_USRTMP;
  263.             break;
  264.  
  265.           case ESM_DEADLETTER:
  266.             /*
  267.             **  Save the message in dead.letter.
  268.             **    If we weren't mailing back, and the user is
  269.             **    local, we should save the message in
  270.             **    ~/dead.letter so that the poor person doesn't
  271.             **    have to type it over again -- and we all know
  272.             **    what poor typists UNIX users are.
  273.             */
  274.  
  275.             p = NULL;
  276.             if (e->e_from.q_mailer == LocalMailer)
  277.             {
  278.                 if (e->e_from.q_home != NULL)
  279.                     p = e->e_from.q_home;
  280.                 else if ((pw = getpwnam(e->e_from.q_user)) != NULL)
  281.                     p = pw->pw_dir;
  282.             }
  283.             if (p == NULL)
  284.             {
  285.                 syserr("Can't return mail to %s", e->e_from.q_paddr);
  286.                 state = ESM_MAIL;
  287.                 break;
  288.             }
  289.             if (e->e_dfp != NULL)
  290.             {
  291.                 auto ADDRESS *q;
  292.                 bool oldverb = Verbose;
  293.  
  294.                 /* we have a home directory; open dead.letter */
  295.                 define('z', p, e);
  296.                 expand("\001z/dead.letter", buf, &buf[sizeof buf - 1], e);
  297.                 Verbose = TRUE;
  298.                 message(Arpa_Info, "Saving message in %s", buf);
  299.                 Verbose = oldverb;
  300.                 e->e_to = buf;
  301.                 q = NULL;
  302.                 sendtolist(buf, (ADDRESS *) NULL, &q);
  303.                 if (deliver(e, q) == 0)
  304.                     state = ESM_DONE;
  305.                 else
  306.                     state = ESM_MAIL;
  307.             }
  308.             else
  309.             {
  310.                 /* no data file -- try mailing back */
  311.                 state = ESM_MAIL;
  312.             }
  313.             break;
  314.  
  315.           case ESM_USRTMP:
  316.             /*
  317.             **  Log the mail in /usr/tmp/dead.letter.
  318.             */
  319.  
  320.             fp = dfopen("/usr/tmp/dead.letter", "a");
  321.             if (fp == NULL)
  322.             {
  323.                 state = ESM_PANIC;
  324.                 break;
  325.             }
  326.  
  327.             putfromline(fp, ProgMailer);
  328.             (*e->e_puthdr)(fp, ProgMailer, e);
  329.             putline("\n", fp, ProgMailer);
  330.             (*e->e_putbody)(fp, ProgMailer, e);
  331.             putline("\n", fp, ProgMailer);
  332.             (void) fflush(fp);
  333.             state = ferror(fp) ? ESM_PANIC : ESM_DONE;
  334.             (void) fclose(fp);
  335.             break;
  336.  
  337.           default:
  338.             syserr("savemail: unknown state %d", state);
  339.  
  340.             /* fall through ... */
  341.  
  342.           case ESM_PANIC:
  343.             syserr("savemail: HELP!!!!");
  344. # ifdef LOG
  345.             if (LogLevel >= 1)
  346.                 syslog(LOG_ALERT, "savemail: HELP!!!!");
  347. # endif LOG
  348.  
  349.             /* leave the locked queue & transcript files around */
  350.             exit(EX_SOFTWARE);
  351.         }
  352.     }
  353. }
  354. /*
  355. **  RETURNTOSENDER -- return a message to the sender with an error.
  356. **
  357. **    Parameters:
  358. **        msg -- the explanatory message.
  359. **        returnq -- the queue of people to send the message to.
  360. **        sendbody -- if TRUE, also send back the body of the
  361. **            message; otherwise just send the header.
  362. **
  363. **    Returns:
  364. **        zero -- if everything went ok.
  365. **        else -- some error.
  366. **
  367. **    Side Effects:
  368. **        Returns the current message to the sender via
  369. **        mail.
  370. */
  371.  
  372. static bool    SendBody;
  373.  
  374. #define MAXRETURNS    6    /* max depth of returning messages */
  375.  
  376. returntosender(msg, returnq, sendbody)
  377.     char *msg;
  378.     ADDRESS *returnq;
  379.     bool sendbody;
  380. {
  381.     char buf[MAXNAME];
  382.     extern putheader(), errbody();
  383.     register ENVELOPE *ee;
  384.     extern ENVELOPE *newenvelope();
  385.     ENVELOPE errenvelope;
  386.     static int returndepth;
  387.     register ADDRESS *q;
  388.  
  389.     if (tTd(6, 1))
  390.     {
  391.         printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n",
  392.                msg, returndepth, CurEnv);
  393.         printf("\treturnq=");
  394.         printaddr(returnq, TRUE);
  395.     }
  396.  
  397.     if (++returndepth >= MAXRETURNS)
  398.     {
  399.         if (returndepth != MAXRETURNS)
  400.             syserr("returntosender: infinite recursion on %s", returnq->q_paddr);
  401.         /* don't "unrecurse" and fake a clean exit */
  402.         /* returndepth--; */
  403.         return (0);
  404.     }
  405.  
  406.     SendBody = sendbody;
  407.     define('g', "\001f", CurEnv);
  408.     ee = newenvelope(&errenvelope);
  409.     define('a', "\001b", ee);
  410.     ee->e_puthdr = putheader;
  411.     ee->e_putbody = errbody;
  412.     ee->e_flags |= EF_RESPONSE;
  413.     if (!bitset(EF_OLDSTYLE, CurEnv->e_flags))
  414.         ee->e_flags &= ~EF_OLDSTYLE;
  415.     ee->e_sendqueue = returnq;
  416.     openxscript(ee);
  417.     for (q = returnq; q != NULL; q = q->q_next)
  418.     {
  419.         if (q->q_alias == NULL)
  420.             addheader("to", q->q_paddr, ee);
  421.     }
  422.  
  423.     (void) sprintf(buf, "Returned mail: %s", msg);
  424.     addheader("subject", buf, ee);
  425.  
  426.     /* fake up an address header for the from person */
  427.     expand("\001n", buf, &buf[sizeof buf - 1], CurEnv);
  428.     if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL)
  429.     {
  430.         syserr("Can't parse myself!");
  431.         ExitStat = EX_SOFTWARE;
  432.         returndepth--;
  433.         return (-1);
  434.     }
  435.     loweraddr(&ee->e_from);
  436.  
  437.     /* push state into submessage */
  438.     CurEnv = ee;
  439.     define('f', "\001n", ee);
  440.     define('x', "Mail Delivery Subsystem", ee);
  441.     eatheader(ee);
  442.  
  443.     /* actually deliver the error message */
  444.     sendall(ee, SM_DEFAULT);
  445.  
  446.     /* restore state */
  447.     dropenvelope(ee);
  448.     CurEnv = CurEnv->e_parent;
  449.     returndepth--;
  450.  
  451.     /* should check for delivery errors here */
  452.     return (0);
  453. }
  454. /*
  455. **  ERRBODY -- output the body of an error message.
  456. **
  457. **    Typically this is a copy of the transcript plus a copy of the
  458. **    original offending message.
  459. **
  460. **    Parameters:
  461. **        fp -- the output file.
  462. **        m -- the mailer to output to.
  463. **        e -- the envelope we are working in.
  464. **
  465. **    Returns:
  466. **        none
  467. **
  468. **    Side Effects:
  469. **        Outputs the body of an error message.
  470. */
  471.  
  472. errbody(fp, m, e)
  473.     register FILE *fp;
  474.     register struct mailer *m;
  475.     register ENVELOPE *e;
  476. {
  477.     register FILE *xfile;
  478.     char buf[MAXLINE];
  479.     char *p;
  480.  
  481.     /*
  482.     **  Output transcript of errors
  483.     */
  484.  
  485.     (void) fflush(stdout);
  486.     p = queuename(e->e_parent, 'x');
  487.     if ((xfile = fopen(p, "r")) == NULL)
  488.     {
  489.         syserr("Cannot open %s", p);
  490.         fprintf(fp, "  ----- Transcript of session is unavailable -----\n");
  491.     }
  492.     else
  493.     {
  494.         fprintf(fp, "   ----- Transcript of session follows -----\n");
  495.         if (e->e_xfp != NULL)
  496.             (void) fflush(e->e_xfp);
  497.         while (fgets(buf, sizeof buf, xfile) != NULL)
  498.             putline(buf, fp, m);
  499.         (void) fclose(xfile);
  500.     }
  501.     errno = 0;
  502.  
  503.     /*
  504.     **  Output text of original message
  505.     */
  506.  
  507.     if (NoReturn)
  508.         fprintf(fp, "\n   ----- Return message suppressed -----\n\n");
  509.     else if (e->e_parent->e_dfp != NULL)
  510.     {
  511.         if (SendBody)
  512.         {
  513.             putline("\n", fp, m);
  514.             putline("   ----- Unsent message follows -----\n", fp, m);
  515.             (void) fflush(fp);
  516.             putheader(fp, m, e->e_parent);
  517.             putline("\n", fp, m);
  518.             putbody(fp, m, e->e_parent);
  519.         }
  520.         else
  521.         {
  522.             putline("\n", fp, m);
  523.             putline("  ----- Message header follows -----\n", fp, m);
  524.             (void) fflush(fp);
  525.             putheader(fp, m, e->e_parent);
  526.         }
  527.     }
  528.     else
  529.     {
  530.         putline("\n", fp, m);
  531.         putline("  ----- No message was collected -----\n", fp, m);
  532.         putline("\n", fp, m);
  533.     }
  534.  
  535.     /*
  536.     **  Cleanup and exit
  537.     */
  538.  
  539.     if (errno != 0)
  540.         syserr("errbody: I/O error");
  541. }
  542.