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