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