122711Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333731Sbostic * Copyright (c) 1988 Regents of the University of California. 433731Sbostic * All rights reserved. 533731Sbostic * 633731Sbostic * Redistribution and use in source and binary forms are permitted 734921Sbostic * provided that the above copyright notice and this paragraph are 834921Sbostic * duplicated in all such forms and that any documentation, 934921Sbostic * advertising materials, and other materials related to such 1034921Sbostic * distribution and use acknowledge that the software was developed 1134921Sbostic * by the University of California, Berkeley. The name of the 1234921Sbostic * University may not be used to endorse or promote products derived 1334921Sbostic * from this software without specific prior written permission. 1434921Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1534921Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1634921Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1733731Sbostic */ 1822711Sdist 1922711Sdist #ifndef lint 20*36928Sbostic static char sccsid[] = "@(#)savemail.c 5.12 (Berkeley) 02/27/89"; 2133731Sbostic #endif /* not lint */ 2222711Sdist 23*36928Sbostic # include <sys/types.h> 24297Seric # include <pwd.h> 253313Seric # include "sendmail.h" 26297Seric 27297Seric /* 28297Seric ** SAVEMAIL -- Save mail on error 29297Seric ** 309375Seric ** If mailing back errors, mail it back to the originator 31297Seric ** together with an error message; otherwise, just put it in 32297Seric ** dead.letter in the user's home directory (if he exists on 33297Seric ** this machine). 34297Seric ** 35297Seric ** Parameters: 369337Seric ** e -- the envelope containing the message in error. 37297Seric ** 38297Seric ** Returns: 39297Seric ** none 40297Seric ** 41297Seric ** Side Effects: 42297Seric ** Saves the letter, by writing or mailing it back to the 43297Seric ** sender, or by putting it in dead.letter in her home 44297Seric ** directory. 45297Seric */ 46297Seric 4724942Seric /* defines for state machine */ 4824942Seric # define ESM_REPORT 0 /* report to sender's terminal */ 4924942Seric # define ESM_MAIL 1 /* mail back to sender */ 5024942Seric # define ESM_QUIET 2 /* messages have already been returned */ 5124942Seric # define ESM_DEADLETTER 3 /* save in ~/dead.letter */ 5224942Seric # define ESM_POSTMASTER 4 /* return to postmaster */ 5324942Seric # define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */ 5424942Seric # define ESM_PANIC 6 /* leave the locked queue/transcript files */ 5524942Seric # define ESM_DONE 7 /* the message is successfully delivered */ 5624942Seric 5724942Seric 589337Seric savemail(e) 599337Seric register ENVELOPE *e; 60297Seric { 61297Seric register struct passwd *pw; 6224942Seric register FILE *fp; 6324942Seric int state; 6424942Seric auto ADDRESS *q; 65297Seric char buf[MAXLINE+1]; 66297Seric extern struct passwd *getpwnam(); 67297Seric register char *p; 68297Seric extern char *ttypath(); 695846Seric typedef int (*fnptr)(); 70297Seric 717676Seric if (tTd(6, 1)) 7224979Seric printf("\nsavemail, ErrorMode = %c\n", ErrorMode); 737361Seric 749375Seric if (bitset(EF_RESPONSE, e->e_flags)) 75297Seric return; 769337Seric if (e->e_class < 0) 776978Seric { 787053Seric message(Arpa_Info, "Dumping junk mail"); 796978Seric return; 806978Seric } 817053Seric ForceMail = TRUE; 829337Seric e->e_flags &= ~EF_FATALERRS; 83297Seric 84297Seric /* 85297Seric ** In the unhappy event we don't know who to return the mail 86297Seric ** to, make someone up. 87297Seric */ 88297Seric 899337Seric if (e->e_from.q_paddr == NULL) 90297Seric { 9111447Seric if (parseaddr("root", &e->e_from, 0, '\0') == NULL) 92297Seric { 93297Seric syserr("Cannot parse root!"); 94297Seric ExitStat = EX_SOFTWARE; 95297Seric finis(); 96297Seric } 97297Seric } 989337Seric e->e_to = NULL; 99297Seric 100297Seric /* 10124942Seric ** Basic state machine. 10224942Seric ** 10324942Seric ** This machine runs through the following states: 10424942Seric ** 10524942Seric ** ESM_QUIET Errors have already been printed iff the 10624942Seric ** sender is local. 10724942Seric ** ESM_REPORT Report directly to the sender's terminal. 10824942Seric ** ESM_MAIL Mail response to the sender. 10924942Seric ** ESM_DEADLETTER Save response in ~/dead.letter. 11024942Seric ** ESM_POSTMASTER Mail response to the postmaster. 11124942Seric ** ESM_PANIC Save response anywhere possible. 112297Seric */ 113297Seric 11424942Seric /* determine starting state */ 11524942Seric switch (ErrorMode) 116297Seric { 11724942Seric case EM_WRITE: 11824942Seric state = ESM_REPORT; 11924942Seric break; 12024942Seric 12124942Seric case EM_BERKNET: 12224942Seric /* mail back, but return o.k. exit status */ 123401Seric ExitStat = EX_OK; 12424942Seric 12524942Seric /* fall through.... */ 12624942Seric 12724942Seric case EM_MAIL: 12824942Seric state = ESM_MAIL; 12924942Seric break; 13024942Seric 13124942Seric case EM_PRINT: 13224979Seric case '\0': 13324942Seric state = ESM_QUIET; 13424942Seric break; 13524942Seric 13624942Seric case EM_QUIET: 13724942Seric /* no need to return anything at all */ 13824942Seric return; 13924979Seric 14024979Seric default: 14124979Seric syserr("savemail: ErrorMode x%x\n"); 14224979Seric state = ESM_MAIL; 14324979Seric break; 144297Seric } 145297Seric 14624942Seric while (state != ESM_DONE) 147297Seric { 14824979Seric if (tTd(6, 5)) 14924979Seric printf(" state %d\n", state); 15024979Seric 15124942Seric switch (state) 152297Seric { 15324979Seric case ESM_QUIET: 15424979Seric if (e->e_from.q_mailer == LocalMailer) 15524979Seric state = ESM_DEADLETTER; 15624979Seric else 15724979Seric state = ESM_MAIL; 15824979Seric break; 15924979Seric 16024942Seric case ESM_REPORT: 16124942Seric 16224942Seric /* 16324942Seric ** If the user is still logged in on the same terminal, 16424942Seric ** then write the error messages back to hir (sic). 16524942Seric */ 16624942Seric 16724942Seric p = ttypath(); 16824942Seric if (p == NULL || freopen(p, "w", stdout) == NULL) 16924942Seric { 17024942Seric state = ESM_MAIL; 17124942Seric break; 17224942Seric } 17324942Seric 17416152Seric expand("\001n", buf, &buf[sizeof buf - 1], e); 1759375Seric printf("\r\nMessage from %s...\r\n", buf); 1769375Seric printf("Errors occurred while sending mail.\r\n"); 1779542Seric if (e->e_xfp != NULL) 1789375Seric { 1799542Seric (void) fflush(e->e_xfp); 18024942Seric fp = fopen(queuename(e, 'x'), "r"); 1819375Seric } 1829375Seric else 18324942Seric fp = NULL; 18424942Seric if (fp == NULL) 1859375Seric { 1869337Seric syserr("Cannot open %s", queuename(e, 'x')); 1879375Seric printf("Transcript of session is unavailable.\r\n"); 1889375Seric } 1899375Seric else 1909375Seric { 1919375Seric printf("Transcript follows:\r\n"); 19224942Seric while (fgets(buf, sizeof buf, fp) != NULL && 1939375Seric !ferror(stdout)) 1949375Seric fputs(buf, stdout); 19524942Seric (void) fclose(fp); 1969375Seric } 19724942Seric printf("Original message will be saved in dead.letter.\r\n"); 19824942Seric state = ESM_DEADLETTER; 19924942Seric break; 200297Seric 20124942Seric case ESM_MAIL: 20224942Seric case ESM_POSTMASTER: 20324942Seric /* 20424942Seric ** If mailing back, do it. 20524942Seric ** Throw away all further output. Don't alias, 20624942Seric ** since this could cause loops, e.g., if joe 20724942Seric ** mails to joe@x, and for some reason the network 20824942Seric ** for @x is down, then the response gets sent to 20924942Seric ** joe@x, which gives a response, etc. Also force 21024942Seric ** the mail to be delivered even if a version of 21124942Seric ** it has already been sent to the sender. 21224942Seric */ 213297Seric 21424942Seric if (state == ESM_MAIL) 21524942Seric { 21624942Seric if (e->e_errorqueue == NULL) 21724942Seric sendtolist(e->e_from.q_paddr, 21824942Seric (ADDRESS *) NULL, 21924942Seric &e->e_errorqueue); 22024981Seric 22124981Seric /* deliver a cc: to the postmaster if desired */ 22224981Seric if (PostMasterCopy != NULL) 22324981Seric sendtolist(PostMasterCopy, 22424981Seric (ADDRESS *) NULL, 22524981Seric &e->e_errorqueue); 22624942Seric q = e->e_errorqueue; 22724942Seric } 22824942Seric else 22924942Seric { 23024955Seric if (parseaddr("postmaster", q, 0, '\0') == NULL) 23124942Seric { 23224942Seric syserr("cannot parse postmaster!"); 23324942Seric ExitStat = EX_SOFTWARE; 23424942Seric state = ESM_USRTMP; 23524942Seric break; 23624942Seric } 23724942Seric } 23824942Seric if (returntosender(e->e_message != NULL ? e->e_message : 23924942Seric "Unable to deliver mail", 24024942Seric q, TRUE) == 0) 24124942Seric { 24224942Seric state = ESM_DONE; 24324942Seric break; 24424942Seric } 245297Seric 24624942Seric state = state == ESM_MAIL ? ESM_POSTMASTER : ESM_USRTMP; 24724942Seric break; 248297Seric 24924942Seric case ESM_DEADLETTER: 25024942Seric /* 25124942Seric ** Save the message in dead.letter. 25224942Seric ** If we weren't mailing back, and the user is 25324942Seric ** local, we should save the message in 25424942Seric ** ~/dead.letter so that the poor person doesn't 25524942Seric ** have to type it over again -- and we all know 25624942Seric ** what poor typists UNIX users are. 25724942Seric */ 2585315Seric 25924942Seric p = NULL; 26024942Seric if (e->e_from.q_mailer == LocalMailer) 26124942Seric { 26224942Seric if (e->e_from.q_home != NULL) 26324942Seric p = e->e_from.q_home; 26424942Seric else if ((pw = getpwnam(e->e_from.q_user)) != NULL) 26524942Seric p = pw->pw_dir; 26624942Seric } 26724942Seric if (p == NULL) 26824942Seric { 26924942Seric syserr("Can't return mail to %s", e->e_from.q_paddr); 27024942Seric state = ESM_MAIL; 27124942Seric break; 27224942Seric } 27324942Seric if (e->e_dfp != NULL) 27424942Seric { 27524942Seric auto ADDRESS *q; 27624942Seric bool oldverb = Verbose; 27724942Seric 27824942Seric /* we have a home directory; open dead.letter */ 27924942Seric define('z', p, e); 28024942Seric expand("\001z/dead.letter", buf, &buf[sizeof buf - 1], e); 28124942Seric Verbose = TRUE; 28224942Seric message(Arpa_Info, "Saving message in %s", buf); 28324942Seric Verbose = oldverb; 28424942Seric e->e_to = buf; 28524942Seric q = NULL; 28624942Seric sendtolist(buf, (ADDRESS *) NULL, &q); 28724942Seric if (deliver(e, q) == 0) 28824942Seric state = ESM_DONE; 28924942Seric else 29024942Seric state = ESM_MAIL; 29124942Seric } 29225569Seric else 29325569Seric { 29425569Seric /* no data file -- try mailing back */ 29525569Seric state = ESM_MAIL; 29625569Seric } 29724942Seric break; 29824942Seric 29924942Seric case ESM_USRTMP: 30024942Seric /* 30124942Seric ** Log the mail in /usr/tmp/dead.letter. 30224942Seric */ 30324942Seric 30424942Seric fp = dfopen("/usr/tmp/dead.letter", "a"); 30524942Seric if (fp == NULL) 30624942Seric { 30724942Seric state = ESM_PANIC; 30824942Seric break; 30924942Seric } 31024942Seric 31124942Seric putfromline(fp, ProgMailer); 31224942Seric (*e->e_puthdr)(fp, ProgMailer, e); 31324942Seric putline("\n", fp, ProgMailer); 31424942Seric (*e->e_putbody)(fp, ProgMailer, e); 31524942Seric putline("\n", fp, ProgMailer); 31624942Seric (void) fflush(fp); 31724942Seric state = ferror(fp) ? ESM_PANIC : ESM_DONE; 31824942Seric (void) fclose(fp); 31924942Seric break; 32024942Seric 32124942Seric default: 32224942Seric syserr("savemail: unknown state %d", state); 32324942Seric 32424942Seric /* fall through ... */ 32524942Seric 32624942Seric case ESM_PANIC: 32724942Seric syserr("savemail: HELP!!!!"); 32824942Seric # ifdef LOG 32924942Seric if (LogLevel >= 1) 33024942Seric syslog(LOG_ALERT, "savemail: HELP!!!!"); 33124942Seric # endif LOG 33224942Seric 33324942Seric /* leave the locked queue & transcript files around */ 33424942Seric exit(EX_SOFTWARE); 33524942Seric } 336297Seric } 337297Seric } 338297Seric /* 3394633Seric ** RETURNTOSENDER -- return a message to the sender with an error. 3404633Seric ** 3414633Seric ** Parameters: 3424633Seric ** msg -- the explanatory message. 34316479Seric ** returnq -- the queue of people to send the message to. 3445984Seric ** sendbody -- if TRUE, also send back the body of the 3455984Seric ** message; otherwise just send the header. 3464633Seric ** 3474633Seric ** Returns: 3484633Seric ** zero -- if everything went ok. 3494633Seric ** else -- some error. 3504633Seric ** 3514633Seric ** Side Effects: 3524633Seric ** Returns the current message to the sender via 3534633Seric ** mail. 3544633Seric */ 3554633Seric 3565984Seric static bool SendBody; 3574633Seric 3587045Seric #define MAXRETURNS 6 /* max depth of returning messages */ 3597045Seric 36016479Seric returntosender(msg, returnq, sendbody) 3614633Seric char *msg; 36216479Seric ADDRESS *returnq; 3635984Seric bool sendbody; 3644633Seric { 3654633Seric char buf[MAXNAME]; 3666978Seric extern putheader(), errbody(); 3676978Seric register ENVELOPE *ee; 3686978Seric extern ENVELOPE *newenvelope(); 3696978Seric ENVELOPE errenvelope; 3707045Seric static int returndepth; 3719375Seric register ADDRESS *q; 3724633Seric 3737676Seric if (tTd(6, 1)) 3747287Seric { 3757287Seric printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n", 3767287Seric msg, returndepth, CurEnv); 37724942Seric printf("\treturnq="); 37816479Seric printaddr(returnq, TRUE); 3797287Seric } 3807287Seric 3817045Seric if (++returndepth >= MAXRETURNS) 3827045Seric { 3837045Seric if (returndepth != MAXRETURNS) 38416479Seric syserr("returntosender: infinite recursion on %s", returnq->q_paddr); 3857045Seric /* don't "unrecurse" and fake a clean exit */ 3867045Seric /* returndepth--; */ 3877045Seric return (0); 3887045Seric } 3897045Seric 3905984Seric SendBody = sendbody; 39116152Seric define('g', "\001f", CurEnv); 3926978Seric ee = newenvelope(&errenvelope); 39324942Seric define('a', "\001b", ee); 3946978Seric ee->e_puthdr = putheader; 3956978Seric ee->e_putbody = errbody; 3969375Seric ee->e_flags |= EF_RESPONSE; 39716479Seric ee->e_sendqueue = returnq; 3989542Seric openxscript(ee); 39916479Seric for (q = returnq; q != NULL; q = q->q_next) 4009375Seric { 4019375Seric if (q->q_alias == NULL) 4029375Seric addheader("to", q->q_paddr, ee); 4039375Seric } 40424942Seric 40510845Seric (void) sprintf(buf, "Returned mail: %s", msg); 40610106Seric addheader("subject", buf, ee); 4074633Seric 4084633Seric /* fake up an address header for the from person */ 40916152Seric expand("\001n", buf, &buf[sizeof buf - 1], CurEnv); 41011447Seric if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL) 4114633Seric { 4124633Seric syserr("Can't parse myself!"); 4134633Seric ExitStat = EX_SOFTWARE; 4147045Seric returndepth--; 4154633Seric return (-1); 4164633Seric } 41716159Seric loweraddr(&ee->e_from); 4185984Seric 4196978Seric /* push state into submessage */ 4206978Seric CurEnv = ee; 42116152Seric define('f', "\001n", ee); 4229375Seric define('x', "Mail Delivery Subsystem", ee); 42311291Seric eatheader(ee); 4245984Seric 4256978Seric /* actually deliver the error message */ 42614876Seric sendall(ee, SM_DEFAULT); 4276978Seric 4286978Seric /* restore state */ 4297811Seric dropenvelope(ee); 4306978Seric CurEnv = CurEnv->e_parent; 4317045Seric returndepth--; 4326978Seric 4337045Seric /* should check for delivery errors here */ 4344633Seric return (0); 4354633Seric } 4364633Seric /* 4376978Seric ** ERRBODY -- output the body of an error message. 4386978Seric ** 4396978Seric ** Typically this is a copy of the transcript plus a copy of the 4406978Seric ** original offending message. 4416978Seric ** 442297Seric ** Parameters: 443297Seric ** fp -- the output file. 44410170Seric ** m -- the mailer to output to. 4459542Seric ** e -- the envelope we are working in. 446297Seric ** 447297Seric ** Returns: 448297Seric ** none 449297Seric ** 450297Seric ** Side Effects: 4516978Seric ** Outputs the body of an error message. 452297Seric */ 453297Seric 45410170Seric errbody(fp, m, e) 455297Seric register FILE *fp; 4564318Seric register struct mailer *m; 4579542Seric register ENVELOPE *e; 458297Seric { 4596978Seric register FILE *xfile; 4603189Seric char buf[MAXLINE]; 4619337Seric char *p; 462297Seric 4639057Seric /* 4649057Seric ** Output transcript of errors 4659057Seric */ 4669057Seric 4674086Seric (void) fflush(stdout); 4689542Seric p = queuename(e->e_parent, 'x'); 4699337Seric if ((xfile = fopen(p, "r")) == NULL) 4709057Seric { 4719337Seric syserr("Cannot open %s", p); 4729057Seric fprintf(fp, " ----- Transcript of session is unavailable -----\n"); 4739057Seric } 4749057Seric else 4759057Seric { 4769057Seric fprintf(fp, " ----- Transcript of session follows -----\n"); 4779542Seric if (e->e_xfp != NULL) 4789542Seric (void) fflush(e->e_xfp); 4799057Seric while (fgets(buf, sizeof buf, xfile) != NULL) 48010170Seric putline(buf, fp, m); 4819057Seric (void) fclose(xfile); 4829057Seric } 483297Seric errno = 0; 4844318Seric 4854318Seric /* 4864318Seric ** Output text of original message 4874318Seric */ 4884318Seric 4894289Seric if (NoReturn) 4904289Seric fprintf(fp, "\n ----- Return message suppressed -----\n\n"); 4919542Seric else if (e->e_parent->e_dfp != NULL) 4924199Seric { 4935984Seric if (SendBody) 4945984Seric { 49510170Seric putline("\n", fp, m); 49610170Seric putline(" ----- Unsent message follows -----\n", fp, m); 4975984Seric (void) fflush(fp); 49810170Seric putheader(fp, m, e->e_parent); 49910170Seric putline("\n", fp, m); 50010170Seric putbody(fp, m, e->e_parent); 5015984Seric } 5025984Seric else 5035984Seric { 50410170Seric putline("\n", fp, m); 50510170Seric putline(" ----- Message header follows -----\n", fp, m); 5065984Seric (void) fflush(fp); 50710170Seric putheader(fp, m, e->e_parent); 5085984Seric } 5094199Seric } 5104199Seric else 51110170Seric { 51210170Seric putline("\n", fp, m); 51310170Seric putline(" ----- No message was collected -----\n", fp, m); 51410170Seric putline("\n", fp, m); 51510170Seric } 5164318Seric 5174318Seric /* 5184318Seric ** Cleanup and exit 5194318Seric */ 5204318Seric 521297Seric if (errno != 0) 5226978Seric syserr("errbody: I/O error"); 523297Seric } 524