122711Sdist /* 2*34921Sbostic * 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 7*34921Sbostic * provided that the above copyright notice and this paragraph are 8*34921Sbostic * duplicated in all such forms and that any documentation, 9*34921Sbostic * advertising materials, and other materials related to such 10*34921Sbostic * distribution and use acknowledge that the software was developed 11*34921Sbostic * by the University of California, Berkeley. The name of the 12*34921Sbostic * University may not be used to endorse or promote products derived 13*34921Sbostic * from this software without specific prior written permission. 14*34921Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 15*34921Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 16*34921Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1733731Sbostic */ 1822711Sdist 1922711Sdist #ifndef lint 20*34921Sbostic static char sccsid[] = "@(#)savemail.c 5.9 (Berkeley) 06/30/88"; 2133731Sbostic #endif /* not lint */ 2222711Sdist 23297Seric # include <pwd.h> 243313Seric # include "sendmail.h" 25297Seric 26297Seric /* 27297Seric ** SAVEMAIL -- Save mail on error 28297Seric ** 299375Seric ** If mailing back errors, mail it back to the originator 30297Seric ** together with an error message; otherwise, just put it in 31297Seric ** dead.letter in the user's home directory (if he exists on 32297Seric ** this machine). 33297Seric ** 34297Seric ** Parameters: 359337Seric ** e -- the envelope containing the message in error. 36297Seric ** 37297Seric ** Returns: 38297Seric ** none 39297Seric ** 40297Seric ** Side Effects: 41297Seric ** Saves the letter, by writing or mailing it back to the 42297Seric ** sender, or by putting it in dead.letter in her home 43297Seric ** directory. 44297Seric */ 45297Seric 4624942Seric /* defines for state machine */ 4724942Seric # define ESM_REPORT 0 /* report to sender's terminal */ 4824942Seric # define ESM_MAIL 1 /* mail back to sender */ 4924942Seric # define ESM_QUIET 2 /* messages have already been returned */ 5024942Seric # define ESM_DEADLETTER 3 /* save in ~/dead.letter */ 5124942Seric # define ESM_POSTMASTER 4 /* return to postmaster */ 5224942Seric # define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */ 5324942Seric # define ESM_PANIC 6 /* leave the locked queue/transcript files */ 5424942Seric # define ESM_DONE 7 /* the message is successfully delivered */ 5524942Seric 5624942Seric 579337Seric savemail(e) 589337Seric register ENVELOPE *e; 59297Seric { 60297Seric register struct passwd *pw; 6124942Seric register FILE *fp; 6224942Seric int state; 6324942Seric auto ADDRESS *q; 64297Seric char buf[MAXLINE+1]; 65297Seric extern struct passwd *getpwnam(); 66297Seric register char *p; 67297Seric extern char *ttypath(); 685846Seric typedef int (*fnptr)(); 69297Seric 707361Seric # ifdef DEBUG 717676Seric if (tTd(6, 1)) 7224979Seric printf("\nsavemail, ErrorMode = %c\n", ErrorMode); 737361Seric # endif DEBUG 747361Seric 759375Seric if (bitset(EF_RESPONSE, e->e_flags)) 76297Seric return; 779337Seric if (e->e_class < 0) 786978Seric { 797053Seric message(Arpa_Info, "Dumping junk mail"); 806978Seric return; 816978Seric } 827053Seric ForceMail = TRUE; 839337Seric e->e_flags &= ~EF_FATALERRS; 84297Seric 85297Seric /* 86297Seric ** In the unhappy event we don't know who to return the mail 87297Seric ** to, make someone up. 88297Seric */ 89297Seric 909337Seric if (e->e_from.q_paddr == NULL) 91297Seric { 9211447Seric if (parseaddr("root", &e->e_from, 0, '\0') == NULL) 93297Seric { 94297Seric syserr("Cannot parse root!"); 95297Seric ExitStat = EX_SOFTWARE; 96297Seric finis(); 97297Seric } 98297Seric } 999337Seric e->e_to = NULL; 100297Seric 101297Seric /* 10224942Seric ** Basic state machine. 10324942Seric ** 10424942Seric ** This machine runs through the following states: 10524942Seric ** 10624942Seric ** ESM_QUIET Errors have already been printed iff the 10724942Seric ** sender is local. 10824942Seric ** ESM_REPORT Report directly to the sender's terminal. 10924942Seric ** ESM_MAIL Mail response to the sender. 11024942Seric ** ESM_DEADLETTER Save response in ~/dead.letter. 11124942Seric ** ESM_POSTMASTER Mail response to the postmaster. 11224942Seric ** ESM_PANIC Save response anywhere possible. 113297Seric */ 114297Seric 11524942Seric /* determine starting state */ 11624942Seric switch (ErrorMode) 117297Seric { 11824942Seric case EM_WRITE: 11924942Seric state = ESM_REPORT; 12024942Seric break; 12124942Seric 12224942Seric case EM_BERKNET: 12324942Seric /* mail back, but return o.k. exit status */ 124401Seric ExitStat = EX_OK; 12524942Seric 12624942Seric /* fall through.... */ 12724942Seric 12824942Seric case EM_MAIL: 12924942Seric state = ESM_MAIL; 13024942Seric break; 13124942Seric 13224942Seric case EM_PRINT: 13324979Seric case '\0': 13424942Seric state = ESM_QUIET; 13524942Seric break; 13624942Seric 13724942Seric case EM_QUIET: 13824942Seric /* no need to return anything at all */ 13924942Seric return; 14024979Seric 14124979Seric default: 14224979Seric syserr("savemail: ErrorMode x%x\n"); 14324979Seric state = ESM_MAIL; 14424979Seric break; 145297Seric } 146297Seric 14724942Seric while (state != ESM_DONE) 148297Seric { 14924979Seric # ifdef DEBUG 15024979Seric if (tTd(6, 5)) 15124979Seric printf(" state %d\n", state); 15224979Seric # endif DEBUG 15324979Seric 15424942Seric switch (state) 155297Seric { 15624979Seric case ESM_QUIET: 15724979Seric if (e->e_from.q_mailer == LocalMailer) 15824979Seric state = ESM_DEADLETTER; 15924979Seric else 16024979Seric state = ESM_MAIL; 16124979Seric break; 16224979Seric 16324942Seric case ESM_REPORT: 16424942Seric 16524942Seric /* 16624942Seric ** If the user is still logged in on the same terminal, 16724942Seric ** then write the error messages back to hir (sic). 16824942Seric */ 16924942Seric 17024942Seric p = ttypath(); 17124942Seric if (p == NULL || freopen(p, "w", stdout) == NULL) 17224942Seric { 17324942Seric state = ESM_MAIL; 17424942Seric break; 17524942Seric } 17624942Seric 17716152Seric expand("\001n", buf, &buf[sizeof buf - 1], e); 1789375Seric printf("\r\nMessage from %s...\r\n", buf); 1799375Seric printf("Errors occurred while sending mail.\r\n"); 1809542Seric if (e->e_xfp != NULL) 1819375Seric { 1829542Seric (void) fflush(e->e_xfp); 18324942Seric fp = fopen(queuename(e, 'x'), "r"); 1849375Seric } 1859375Seric else 18624942Seric fp = NULL; 18724942Seric if (fp == NULL) 1889375Seric { 1899337Seric syserr("Cannot open %s", queuename(e, 'x')); 1909375Seric printf("Transcript of session is unavailable.\r\n"); 1919375Seric } 1929375Seric else 1939375Seric { 1949375Seric printf("Transcript follows:\r\n"); 19524942Seric while (fgets(buf, sizeof buf, fp) != NULL && 1969375Seric !ferror(stdout)) 1979375Seric fputs(buf, stdout); 19824942Seric (void) fclose(fp); 1999375Seric } 20024942Seric printf("Original message will be saved in dead.letter.\r\n"); 201297Seric if (ferror(stdout)) 2024086Seric (void) syserr("savemail: stdout: write err"); 20324942Seric state = ESM_DEADLETTER; 20424942Seric break; 205297Seric 20624942Seric case ESM_MAIL: 20724942Seric case ESM_POSTMASTER: 20824942Seric /* 20924942Seric ** If mailing back, do it. 21024942Seric ** Throw away all further output. Don't alias, 21124942Seric ** since this could cause loops, e.g., if joe 21224942Seric ** mails to joe@x, and for some reason the network 21324942Seric ** for @x is down, then the response gets sent to 21424942Seric ** joe@x, which gives a response, etc. Also force 21524942Seric ** the mail to be delivered even if a version of 21624942Seric ** it has already been sent to the sender. 21724942Seric */ 218297Seric 21924942Seric if (state == ESM_MAIL) 22024942Seric { 22124942Seric if (e->e_errorqueue == NULL) 22224942Seric sendtolist(e->e_from.q_paddr, 22324942Seric (ADDRESS *) NULL, 22424942Seric &e->e_errorqueue); 22524981Seric 22624981Seric /* deliver a cc: to the postmaster if desired */ 22724981Seric if (PostMasterCopy != NULL) 22824981Seric sendtolist(PostMasterCopy, 22924981Seric (ADDRESS *) NULL, 23024981Seric &e->e_errorqueue); 23124942Seric q = e->e_errorqueue; 23224942Seric } 23324942Seric else 23424942Seric { 23524955Seric if (parseaddr("postmaster", q, 0, '\0') == NULL) 23624942Seric { 23724942Seric syserr("cannot parse postmaster!"); 23824942Seric ExitStat = EX_SOFTWARE; 23924942Seric state = ESM_USRTMP; 24024942Seric break; 24124942Seric } 24224942Seric } 24324942Seric if (returntosender(e->e_message != NULL ? e->e_message : 24424942Seric "Unable to deliver mail", 24524942Seric q, TRUE) == 0) 24624942Seric { 24724942Seric state = ESM_DONE; 24824942Seric break; 24924942Seric } 250297Seric 25124942Seric state = state == ESM_MAIL ? ESM_POSTMASTER : ESM_USRTMP; 25224942Seric break; 253297Seric 25424942Seric case ESM_DEADLETTER: 25524942Seric /* 25624942Seric ** Save the message in dead.letter. 25724942Seric ** If we weren't mailing back, and the user is 25824942Seric ** local, we should save the message in 25924942Seric ** ~/dead.letter so that the poor person doesn't 26024942Seric ** have to type it over again -- and we all know 26124942Seric ** what poor typists UNIX users are. 26224942Seric */ 2635315Seric 26424942Seric p = NULL; 26524942Seric if (e->e_from.q_mailer == LocalMailer) 26624942Seric { 26724942Seric if (e->e_from.q_home != NULL) 26824942Seric p = e->e_from.q_home; 26924942Seric else if ((pw = getpwnam(e->e_from.q_user)) != NULL) 27024942Seric p = pw->pw_dir; 27124942Seric } 27224942Seric if (p == NULL) 27324942Seric { 27424942Seric syserr("Can't return mail to %s", e->e_from.q_paddr); 27524942Seric state = ESM_MAIL; 27624942Seric break; 27724942Seric } 27824942Seric if (e->e_dfp != NULL) 27924942Seric { 28024942Seric auto ADDRESS *q; 28124942Seric bool oldverb = Verbose; 28224942Seric 28324942Seric /* we have a home directory; open dead.letter */ 28424942Seric define('z', p, e); 28524942Seric expand("\001z/dead.letter", buf, &buf[sizeof buf - 1], e); 28624942Seric Verbose = TRUE; 28724942Seric message(Arpa_Info, "Saving message in %s", buf); 28824942Seric Verbose = oldverb; 28924942Seric e->e_to = buf; 29024942Seric q = NULL; 29124942Seric sendtolist(buf, (ADDRESS *) NULL, &q); 29224942Seric if (deliver(e, q) == 0) 29324942Seric state = ESM_DONE; 29424942Seric else 29524942Seric state = ESM_MAIL; 29624942Seric } 29725569Seric else 29825569Seric { 29925569Seric /* no data file -- try mailing back */ 30025569Seric state = ESM_MAIL; 30125569Seric } 30224942Seric break; 30324942Seric 30424942Seric case ESM_USRTMP: 30524942Seric /* 30624942Seric ** Log the mail in /usr/tmp/dead.letter. 30724942Seric */ 30824942Seric 30924942Seric fp = dfopen("/usr/tmp/dead.letter", "a"); 31024942Seric if (fp == NULL) 31124942Seric { 31224942Seric state = ESM_PANIC; 31324942Seric break; 31424942Seric } 31524942Seric 31624942Seric putfromline(fp, ProgMailer); 31724942Seric (*e->e_puthdr)(fp, ProgMailer, e); 31824942Seric putline("\n", fp, ProgMailer); 31924942Seric (*e->e_putbody)(fp, ProgMailer, e); 32024942Seric putline("\n", fp, ProgMailer); 32124942Seric (void) fflush(fp); 32224942Seric state = ferror(fp) ? ESM_PANIC : ESM_DONE; 32324942Seric (void) fclose(fp); 32424942Seric break; 32524942Seric 32624942Seric default: 32724942Seric syserr("savemail: unknown state %d", state); 32824942Seric 32924942Seric /* fall through ... */ 33024942Seric 33124942Seric case ESM_PANIC: 33224942Seric syserr("savemail: HELP!!!!"); 33324942Seric # ifdef LOG 33424942Seric if (LogLevel >= 1) 33524942Seric syslog(LOG_ALERT, "savemail: HELP!!!!"); 33624942Seric # endif LOG 33724942Seric 33824942Seric /* leave the locked queue & transcript files around */ 33924942Seric exit(EX_SOFTWARE); 34024942Seric } 341297Seric } 342297Seric } 343297Seric /* 3444633Seric ** RETURNTOSENDER -- return a message to the sender with an error. 3454633Seric ** 3464633Seric ** Parameters: 3474633Seric ** msg -- the explanatory message. 34816479Seric ** returnq -- the queue of people to send the message to. 3495984Seric ** sendbody -- if TRUE, also send back the body of the 3505984Seric ** message; otherwise just send the header. 3514633Seric ** 3524633Seric ** Returns: 3534633Seric ** zero -- if everything went ok. 3544633Seric ** else -- some error. 3554633Seric ** 3564633Seric ** Side Effects: 3574633Seric ** Returns the current message to the sender via 3584633Seric ** mail. 3594633Seric */ 3604633Seric 3615984Seric static bool SendBody; 3624633Seric 3637045Seric #define MAXRETURNS 6 /* max depth of returning messages */ 3647045Seric 36516479Seric returntosender(msg, returnq, sendbody) 3664633Seric char *msg; 36716479Seric ADDRESS *returnq; 3685984Seric bool sendbody; 3694633Seric { 3704633Seric char buf[MAXNAME]; 3716978Seric extern putheader(), errbody(); 3726978Seric register ENVELOPE *ee; 3736978Seric extern ENVELOPE *newenvelope(); 3746978Seric ENVELOPE errenvelope; 3757045Seric static int returndepth; 3769375Seric register ADDRESS *q; 3774633Seric 3787287Seric # ifdef DEBUG 3797676Seric if (tTd(6, 1)) 3807287Seric { 3817287Seric printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n", 3827287Seric msg, returndepth, CurEnv); 38324942Seric printf("\treturnq="); 38416479Seric printaddr(returnq, TRUE); 3857287Seric } 3867287Seric # endif DEBUG 3877287Seric 3887045Seric if (++returndepth >= MAXRETURNS) 3897045Seric { 3907045Seric if (returndepth != MAXRETURNS) 39116479Seric syserr("returntosender: infinite recursion on %s", returnq->q_paddr); 3927045Seric /* don't "unrecurse" and fake a clean exit */ 3937045Seric /* returndepth--; */ 3947045Seric return (0); 3957045Seric } 3967045Seric 3975984Seric SendBody = sendbody; 39816152Seric define('g', "\001f", CurEnv); 3996978Seric ee = newenvelope(&errenvelope); 40024942Seric define('a', "\001b", ee); 4016978Seric ee->e_puthdr = putheader; 4026978Seric ee->e_putbody = errbody; 4039375Seric ee->e_flags |= EF_RESPONSE; 40416479Seric ee->e_sendqueue = returnq; 4059542Seric openxscript(ee); 40616479Seric for (q = returnq; q != NULL; q = q->q_next) 4079375Seric { 4089375Seric if (q->q_alias == NULL) 4099375Seric addheader("to", q->q_paddr, ee); 4109375Seric } 41124942Seric 41210845Seric (void) sprintf(buf, "Returned mail: %s", msg); 41310106Seric addheader("subject", buf, ee); 4144633Seric 4154633Seric /* fake up an address header for the from person */ 41616152Seric expand("\001n", buf, &buf[sizeof buf - 1], CurEnv); 41711447Seric if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL) 4184633Seric { 4194633Seric syserr("Can't parse myself!"); 4204633Seric ExitStat = EX_SOFTWARE; 4217045Seric returndepth--; 4224633Seric return (-1); 4234633Seric } 42416159Seric loweraddr(&ee->e_from); 4255984Seric 4266978Seric /* push state into submessage */ 4276978Seric CurEnv = ee; 42816152Seric define('f', "\001n", ee); 4299375Seric define('x', "Mail Delivery Subsystem", ee); 43011291Seric eatheader(ee); 4315984Seric 4326978Seric /* actually deliver the error message */ 43314876Seric sendall(ee, SM_DEFAULT); 4346978Seric 4356978Seric /* restore state */ 4367811Seric dropenvelope(ee); 4376978Seric CurEnv = CurEnv->e_parent; 4387045Seric returndepth--; 4396978Seric 4407045Seric /* should check for delivery errors here */ 4414633Seric return (0); 4424633Seric } 4434633Seric /* 4446978Seric ** ERRBODY -- output the body of an error message. 4456978Seric ** 4466978Seric ** Typically this is a copy of the transcript plus a copy of the 4476978Seric ** original offending message. 4486978Seric ** 449297Seric ** Parameters: 450297Seric ** fp -- the output file. 45110170Seric ** m -- the mailer to output to. 4529542Seric ** e -- the envelope we are working in. 453297Seric ** 454297Seric ** Returns: 455297Seric ** none 456297Seric ** 457297Seric ** Side Effects: 4586978Seric ** Outputs the body of an error message. 459297Seric */ 460297Seric 46110170Seric errbody(fp, m, e) 462297Seric register FILE *fp; 4634318Seric register struct mailer *m; 4649542Seric register ENVELOPE *e; 465297Seric { 4666978Seric register FILE *xfile; 4673189Seric char buf[MAXLINE]; 4689337Seric char *p; 469297Seric 4709057Seric /* 4719057Seric ** Output transcript of errors 4729057Seric */ 4739057Seric 4744086Seric (void) fflush(stdout); 4759542Seric p = queuename(e->e_parent, 'x'); 4769337Seric if ((xfile = fopen(p, "r")) == NULL) 4779057Seric { 4789337Seric syserr("Cannot open %s", p); 4799057Seric fprintf(fp, " ----- Transcript of session is unavailable -----\n"); 4809057Seric } 4819057Seric else 4829057Seric { 4839057Seric fprintf(fp, " ----- Transcript of session follows -----\n"); 4849542Seric if (e->e_xfp != NULL) 4859542Seric (void) fflush(e->e_xfp); 4869057Seric while (fgets(buf, sizeof buf, xfile) != NULL) 48710170Seric putline(buf, fp, m); 4889057Seric (void) fclose(xfile); 4899057Seric } 490297Seric errno = 0; 4914318Seric 4924318Seric /* 4934318Seric ** Output text of original message 4944318Seric */ 4954318Seric 4964289Seric if (NoReturn) 4974289Seric fprintf(fp, "\n ----- Return message suppressed -----\n\n"); 4989542Seric else if (e->e_parent->e_dfp != NULL) 4994199Seric { 5005984Seric if (SendBody) 5015984Seric { 50210170Seric putline("\n", fp, m); 50310170Seric putline(" ----- Unsent message follows -----\n", fp, m); 5045984Seric (void) fflush(fp); 50510170Seric putheader(fp, m, e->e_parent); 50610170Seric putline("\n", fp, m); 50710170Seric putbody(fp, m, e->e_parent); 5085984Seric } 5095984Seric else 5105984Seric { 51110170Seric putline("\n", fp, m); 51210170Seric putline(" ----- Message header follows -----\n", fp, m); 5135984Seric (void) fflush(fp); 51410170Seric putheader(fp, m, e->e_parent); 5155984Seric } 5164199Seric } 5174199Seric else 51810170Seric { 51910170Seric putline("\n", fp, m); 52010170Seric putline(" ----- No message was collected -----\n", fp, m); 52110170Seric putline("\n", fp, m); 52210170Seric } 5234318Seric 5244318Seric /* 5254318Seric ** Cleanup and exit 5264318Seric */ 5274318Seric 528297Seric if (errno != 0) 5296978Seric syserr("errbody: I/O error"); 530297Seric } 531