122711Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 362531Sbostic * Copyright (c) 1988, 1993 462531Sbostic * The Regents of the University of California. All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822711Sdist 922711Sdist #ifndef lint 10*68228Seric static char sccsid[] = "@(#)savemail.c 8.51 (Berkeley) 02/05/95"; 1133731Sbostic #endif /* not lint */ 1222711Sdist 1363937Seric # include "sendmail.h" 14297Seric # include <pwd.h> 15297Seric 16297Seric /* 17297Seric ** SAVEMAIL -- Save mail on error 18297Seric ** 199375Seric ** If mailing back errors, mail it back to the originator 20297Seric ** together with an error message; otherwise, just put it in 21297Seric ** dead.letter in the user's home directory (if he exists on 22297Seric ** this machine). 23297Seric ** 24297Seric ** Parameters: 259337Seric ** e -- the envelope containing the message in error. 2667981Seric ** sendbody -- if TRUE, also send back the body of the 2767981Seric ** message; otherwise just send the header. 28297Seric ** 29297Seric ** Returns: 30297Seric ** none 31297Seric ** 32297Seric ** Side Effects: 33297Seric ** Saves the letter, by writing or mailing it back to the 34297Seric ** sender, or by putting it in dead.letter in her home 35297Seric ** directory. 36297Seric */ 37297Seric 3824942Seric /* defines for state machine */ 3924942Seric # define ESM_REPORT 0 /* report to sender's terminal */ 4024942Seric # define ESM_MAIL 1 /* mail back to sender */ 4124942Seric # define ESM_QUIET 2 /* messages have already been returned */ 4224942Seric # define ESM_DEADLETTER 3 /* save in ~/dead.letter */ 4324942Seric # define ESM_POSTMASTER 4 /* return to postmaster */ 4424942Seric # define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */ 4524942Seric # define ESM_PANIC 6 /* leave the locked queue/transcript files */ 4624942Seric # define ESM_DONE 7 /* the message is successfully delivered */ 4724942Seric 4865174Seric # ifndef _PATH_VARTMP 4965174Seric # define _PATH_VARTMP "/usr/tmp/" 5065174Seric # endif 5124942Seric 5265174Seric 5367981Seric savemail(e, sendbody) 549337Seric register ENVELOPE *e; 5567981Seric bool sendbody; 56297Seric { 57297Seric register struct passwd *pw; 5824942Seric register FILE *fp; 5924942Seric int state; 6059290Seric auto ADDRESS *q = NULL; 6165870Seric register char *p; 6265870Seric MCI mcibuf; 63297Seric char buf[MAXLINE+1]; 64297Seric extern struct passwd *getpwnam(); 65297Seric extern char *ttypath(); 665846Seric typedef int (*fnptr)(); 6764945Seric extern bool writable(); 68297Seric 697676Seric if (tTd(6, 1)) 7058680Seric { 7166317Seric printf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=", 7266317Seric e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id, 7366317Seric ExitStat); 7458680Seric printaddr(&e->e_from, FALSE); 7558680Seric } 767361Seric 7759101Seric if (e->e_id == NULL) 7859101Seric { 7959101Seric /* can't return a message with no id */ 8059101Seric return; 8159101Seric } 8259101Seric 83297Seric /* 84297Seric ** In the unhappy event we don't know who to return the mail 85297Seric ** to, make someone up. 86297Seric */ 87297Seric 889337Seric if (e->e_from.q_paddr == NULL) 89297Seric { 9058733Seric e->e_sender = "Postmaster"; 9164284Seric if (parseaddr(e->e_sender, &e->e_from, 9264284Seric RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL) 93297Seric { 9458733Seric syserr("553 Cannot parse Postmaster!"); 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 */ 11658734Seric switch (e->e_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: 14258734Seric syserr("554 savemail: bogus errormode x%x\n", e->e_errormode); 14324979Seric state = ESM_MAIL; 14424979Seric break; 145297Seric } 146297Seric 14759094Seric /* if this is already an error response, send to postmaster */ 14859094Seric if (bitset(EF_RESPONSE, e->e_flags)) 14959094Seric { 15059094Seric if (e->e_parent != NULL && 15159094Seric bitset(EF_RESPONSE, e->e_parent->e_flags)) 15259094Seric { 15359094Seric /* got an error sending a response -- can it */ 15459094Seric return; 15559094Seric } 15659094Seric state = ESM_POSTMASTER; 15759094Seric } 15859094Seric 15924942Seric while (state != ESM_DONE) 160297Seric { 16124979Seric if (tTd(6, 5)) 16224979Seric printf(" state %d\n", state); 16324979Seric 16424942Seric switch (state) 165297Seric { 16624979Seric case ESM_QUIET: 16767473Seric if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags)) 16824979Seric state = ESM_DEADLETTER; 16924979Seric else 17024979Seric state = ESM_MAIL; 17124979Seric break; 17224979Seric 17324942Seric case ESM_REPORT: 17424942Seric 17524942Seric /* 17624942Seric ** If the user is still logged in on the same terminal, 17724942Seric ** then write the error messages back to hir (sic). 17824942Seric */ 17924942Seric 18024942Seric p = ttypath(); 18124942Seric if (p == NULL || freopen(p, "w", stdout) == NULL) 18224942Seric { 18324942Seric state = ESM_MAIL; 18424942Seric break; 18524942Seric } 18624942Seric 18758050Seric expand("\201n", buf, &buf[sizeof buf - 1], e); 1889375Seric printf("\r\nMessage from %s...\r\n", buf); 1899375Seric printf("Errors occurred while sending mail.\r\n"); 1909542Seric if (e->e_xfp != NULL) 1919375Seric { 1929542Seric (void) fflush(e->e_xfp); 19324942Seric fp = fopen(queuename(e, 'x'), "r"); 1949375Seric } 1959375Seric else 19624942Seric fp = NULL; 19724942Seric if (fp == NULL) 1989375Seric { 1999337Seric syserr("Cannot open %s", queuename(e, 'x')); 2009375Seric printf("Transcript of session is unavailable.\r\n"); 2019375Seric } 2029375Seric else 2039375Seric { 2049375Seric printf("Transcript follows:\r\n"); 20524942Seric while (fgets(buf, sizeof buf, fp) != NULL && 2069375Seric !ferror(stdout)) 2079375Seric fputs(buf, stdout); 20858680Seric (void) xfclose(fp, "savemail transcript", e->e_id); 2099375Seric } 21024942Seric printf("Original message will be saved in dead.letter.\r\n"); 21124942Seric state = ESM_DEADLETTER; 21224942Seric break; 213297Seric 21424942Seric case ESM_MAIL: 21524942Seric /* 21624942Seric ** If mailing back, do it. 21724942Seric ** Throw away all further output. Don't alias, 21824942Seric ** since this could cause loops, e.g., if joe 21924942Seric ** mails to joe@x, and for some reason the network 22024942Seric ** for @x is down, then the response gets sent to 22124942Seric ** joe@x, which gives a response, etc. Also force 22224942Seric ** the mail to be delivered even if a version of 22324942Seric ** it has already been sent to the sender. 22463841Seric ** 22563841Seric ** If this is a configuration or local software 22663841Seric ** error, send to the local postmaster as well, 22763841Seric ** since the originator can't do anything 22863841Seric ** about it anyway. Note that this is a full 22963841Seric ** copy of the message (intentionally) so that 23063841Seric ** the Postmaster can forward things along. 23124942Seric */ 232297Seric 23363841Seric if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE) 23463841Seric { 23563841Seric (void) sendtolist("postmaster", 23667982Seric NULLADDR, &e->e_errorqueue, 0, e); 23763841Seric } 23867939Seric if (!emptyaddr(&e->e_from)) 23963841Seric { 24058680Seric (void) sendtolist(e->e_from.q_paddr, 24167982Seric NULLADDR, &e->e_errorqueue, 0, e); 24263841Seric } 24358680Seric 24463841Seric /* 24563841Seric ** Deliver a non-delivery report to the 24663841Seric ** Postmaster-designate (not necessarily 24763841Seric ** Postmaster). This does not include the 24863841Seric ** body of the message, for privacy reasons. 24963841Seric ** You really shouldn't need this. 25063841Seric */ 25163841Seric 25263849Seric e->e_flags |= EF_PM_NOTIFY; 25358178Seric 25464792Seric /* check to see if there are any good addresses */ 25564792Seric for (q = e->e_errorqueue; q != NULL; q = q->q_next) 25664792Seric if (!bitset(QBADADDR|QDONTSEND, q->q_flags)) 25764792Seric break; 25858680Seric if (q == NULL) 25958680Seric { 26058680Seric /* this is an error-error */ 26158680Seric state = ESM_POSTMASTER; 26258680Seric break; 26358680Seric } 26466303Seric if (returntosender(e->e_message, e->e_errorqueue, 26567981Seric sendbody, e) == 0) 26658680Seric { 26758680Seric state = ESM_DONE; 26858680Seric break; 26958680Seric } 27024981Seric 27158680Seric /* didn't work -- return to postmaster */ 27258680Seric state = ESM_POSTMASTER; 27358680Seric break; 27458306Seric 27558680Seric case ESM_POSTMASTER: 27658680Seric /* 27758680Seric ** Similar to previous case, but to system postmaster. 27858680Seric */ 27958680Seric 28059432Seric q = NULL; 28167982Seric if (sendtolist("postmaster", NULL, &q, 0, e) <= 0) 28224942Seric { 28358680Seric syserr("553 cannot parse postmaster!"); 28458680Seric ExitStat = EX_SOFTWARE; 28558680Seric state = ESM_USRTMP; 28658680Seric break; 28724942Seric } 28867981Seric if (returntosender(e->e_message, q, sendbody, e) == 0) 28924942Seric { 29024942Seric state = ESM_DONE; 29124942Seric break; 29224942Seric } 293297Seric 29458680Seric /* didn't work -- last resort */ 29558680Seric state = ESM_USRTMP; 29624942Seric break; 297297Seric 29824942Seric case ESM_DEADLETTER: 29924942Seric /* 30024942Seric ** Save the message in dead.letter. 30124942Seric ** If we weren't mailing back, and the user is 30224942Seric ** local, we should save the message in 30324942Seric ** ~/dead.letter so that the poor person doesn't 30424942Seric ** have to type it over again -- and we all know 30524942Seric ** what poor typists UNIX users are. 30624942Seric */ 3075315Seric 30824942Seric p = NULL; 30967473Seric if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags)) 31024942Seric { 31124942Seric if (e->e_from.q_home != NULL) 31224942Seric p = e->e_from.q_home; 31324942Seric else if ((pw = getpwnam(e->e_from.q_user)) != NULL) 31424942Seric p = pw->pw_dir; 31524942Seric } 31624942Seric if (p == NULL) 31724942Seric { 31858865Seric /* no local directory */ 31924942Seric state = ESM_MAIL; 32024942Seric break; 32124942Seric } 32224942Seric if (e->e_dfp != NULL) 32324942Seric { 32424942Seric bool oldverb = Verbose; 32524942Seric 32624942Seric /* we have a home directory; open dead.letter */ 32724942Seric define('z', p, e); 32858050Seric expand("\201z/dead.letter", buf, &buf[sizeof buf - 1], e); 32924942Seric Verbose = TRUE; 33058151Seric message("Saving message in %s", buf); 33124942Seric Verbose = oldverb; 33224942Seric e->e_to = buf; 33324942Seric q = NULL; 33467982Seric (void) sendtolist(buf, &e->e_from, &q, 0, e); 33564354Seric if (q != NULL && 33664354Seric !bitset(QBADADDR, q->q_flags) && 33764354Seric deliver(e, q) == 0) 33824942Seric state = ESM_DONE; 33924942Seric else 34024942Seric state = ESM_MAIL; 34124942Seric } 34225569Seric else 34325569Seric { 34425569Seric /* no data file -- try mailing back */ 34525569Seric state = ESM_MAIL; 34625569Seric } 34724942Seric break; 34824942Seric 34924942Seric case ESM_USRTMP: 35024942Seric /* 35124942Seric ** Log the mail in /usr/tmp/dead.letter. 35224942Seric */ 35324942Seric 35457438Seric if (e->e_class < 0) 35557438Seric { 35657438Seric state = ESM_DONE; 35757438Seric break; 35857438Seric } 35957438Seric 36065174Seric strcpy(buf, _PATH_VARTMP); 36165174Seric strcat(buf, "dead.letter"); 36265112Seric if (!writable(buf, NULLADDR, SFF_NOSLINK)) 36364945Seric { 36464945Seric state = ESM_PANIC; 36564945Seric break; 36664945Seric } 36764945Seric fp = dfopen(buf, O_WRONLY|O_CREAT|O_APPEND, FileMode); 36824942Seric if (fp == NULL) 36924942Seric { 37024942Seric state = ESM_PANIC; 37124942Seric break; 37224942Seric } 37324942Seric 37465870Seric bzero(&mcibuf, sizeof mcibuf); 37565870Seric mcibuf.mci_out = fp; 37665870Seric mcibuf.mci_mailer = FileMailer; 37765870Seric if (bitnset(M_7BITS, FileMailer->m_flags)) 37865870Seric mcibuf.mci_flags |= MCIF_7BIT; 37965870Seric 38065870Seric putfromline(&mcibuf, e); 381*68228Seric (*e->e_puthdr)(&mcibuf, e->e_header, e); 382*68228Seric (*e->e_putbody)(&mcibuf, e, NULL); 38365870Seric putline("\n", &mcibuf); 38424942Seric (void) fflush(fp); 38524942Seric state = ferror(fp) ? ESM_PANIC : ESM_DONE; 38667809Seric (void) xfclose(fp, "savemail", buf); 38724942Seric break; 38824942Seric 38924942Seric default: 39058151Seric syserr("554 savemail: unknown state %d", state); 39124942Seric 39224942Seric /* fall through ... */ 39324942Seric 39424942Seric case ESM_PANIC: 39524942Seric /* leave the locked queue & transcript files around */ 39664949Seric syserr("!554 savemail: cannot save rejected email anywhere"); 39724942Seric } 398297Seric } 399297Seric } 400297Seric /* 4014633Seric ** RETURNTOSENDER -- return a message to the sender with an error. 4024633Seric ** 4034633Seric ** Parameters: 4044633Seric ** msg -- the explanatory message. 40516479Seric ** returnq -- the queue of people to send the message to. 4065984Seric ** sendbody -- if TRUE, also send back the body of the 4075984Seric ** message; otherwise just send the header. 40855012Seric ** e -- the current envelope. 4094633Seric ** 4104633Seric ** Returns: 4114633Seric ** zero -- if everything went ok. 4124633Seric ** else -- some error. 4134633Seric ** 4144633Seric ** Side Effects: 4154633Seric ** Returns the current message to the sender via 4164633Seric ** mail. 4174633Seric */ 4184633Seric 4195984Seric static bool SendBody; 4204633Seric 4217045Seric #define MAXRETURNS 6 /* max depth of returning messages */ 42258559Seric #define ERRORFUDGE 100 /* nominal size of error message text */ 4237045Seric 42455012Seric returntosender(msg, returnq, sendbody, e) 4254633Seric char *msg; 42616479Seric ADDRESS *returnq; 4275984Seric bool sendbody; 42855012Seric register ENVELOPE *e; 4294633Seric { 4304633Seric char buf[MAXNAME]; 4316978Seric extern putheader(), errbody(); 4326978Seric register ENVELOPE *ee; 43358680Seric ENVELOPE *oldcur = CurEnv; 4346978Seric ENVELOPE errenvelope; 4357045Seric static int returndepth; 4369375Seric register ADDRESS *q; 437*68228Seric char *p; 4384633Seric 43960010Seric if (returnq == NULL) 44060010Seric return (-1); 44160010Seric 44258966Seric if (msg == NULL) 44358966Seric msg = "Unable to deliver mail"; 44458966Seric 4457676Seric if (tTd(6, 1)) 4467287Seric { 44767987Seric printf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%x, returnq=", 44855012Seric msg, returndepth, e); 44916479Seric printaddr(returnq, TRUE); 45067987Seric if (tTd(6, 20)) 45167987Seric { 45267987Seric printf("Sendq="); 45367987Seric printaddr(e->e_sendqueue, TRUE); 45467987Seric } 4557287Seric } 4567287Seric 4577045Seric if (++returndepth >= MAXRETURNS) 4587045Seric { 4597045Seric if (returndepth != MAXRETURNS) 46058151Seric syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr); 4617045Seric /* don't "unrecurse" and fake a clean exit */ 4627045Seric /* returndepth--; */ 4637045Seric return (0); 4647045Seric } 4657045Seric 4665984Seric SendBody = sendbody; 46758680Seric define('g', e->e_from.q_paddr, e); 46864940Seric define('u', NULL, e); 46967880Seric 47067880Seric /* initialize error envelope */ 47158179Seric ee = newenvelope(&errenvelope, e); 47258050Seric define('a', "\201b", ee); 47359057Seric define('r', "internal", ee); 47459057Seric define('s', "localhost", ee); 47559057Seric define('_', "localhost", ee); 4766978Seric ee->e_puthdr = putheader; 4776978Seric ee->e_putbody = errbody; 47864120Seric ee->e_flags |= EF_RESPONSE|EF_METOO; 47955012Seric if (!bitset(EF_OLDSTYLE, e->e_flags)) 48045155Seric ee->e_flags &= ~EF_OLDSTYLE; 48116479Seric ee->e_sendqueue = returnq; 48264655Seric ee->e_msgsize = ERRORFUDGE; 48367473Seric if (!bitset(EF_NORETURN, e->e_flags)) 48464655Seric ee->e_msgsize += e->e_msgsize; 48564737Seric initsys(ee); 48616479Seric for (q = returnq; q != NULL; q = q->q_next) 4879375Seric { 48859989Seric if (bitset(QBADADDR, q->q_flags)) 48958559Seric continue; 49058559Seric 49168141Seric if (!DontPruneRoutes && pruneroute(q->q_paddr)) 49268141Seric { 49368141Seric register ADDRESS *p; 49468141Seric 49568141Seric parseaddr(q->q_paddr, q, RF_COPYPARSE, '\0', NULL, e); 49668141Seric for (p = returnq; p != NULL; p = p->q_next) 49768141Seric { 49868141Seric if (p != q && sameaddr(p, q)) 49968141Seric q->q_flags |= QDONTSEND; 50068141Seric } 50168141Seric } 50268141Seric 50359989Seric if (!bitset(QDONTSEND, q->q_flags)) 50459989Seric ee->e_nrcpts++; 50558559Seric 5069375Seric if (q->q_alias == NULL) 50767546Seric addheader("To", q->q_paddr, &ee->e_header); 5089375Seric } 50924942Seric 51057642Seric # ifdef LOG 51158020Seric if (LogLevel > 5) 51265054Seric syslog(LOG_INFO, "%s: %s: return to sender: %s", 51357642Seric e->e_id, ee->e_id, msg); 51457642Seric # endif 51557642Seric 51668003Seric if (SendMIMEErrors) 51767261Seric { 51868003Seric addheader("MIME-Version", "1.0", &ee->e_header); 51968003Seric (void) sprintf(buf, "%s.%ld/%s", 52068003Seric ee->e_id, curtime(), MyHostName); 52168003Seric ee->e_msgboundary = newstr(buf); 52268003Seric (void) sprintf(buf, 52368028Seric #ifdef DSN 52468085Seric "multipart/report; report-type=X-delivery-status-1; boundary=\"%s\"", 52568028Seric #else 52668028Seric "multipart/mixed; boundary=\"%s\"", 52768028Seric #endif 52868003Seric ee->e_msgboundary); 52968003Seric addheader("Content-Type", buf, &ee->e_header); 53068003Seric } 531*68228Seric if (strncmp(msg, "Warning:", 8) == 0) 53268003Seric { 53367261Seric addheader("Subject", msg, ee); 534*68228Seric p = "warning-timeout"; 53567261Seric } 53667429Seric else if (strcmp(msg, "Return receipt") == 0) 53767429Seric { 53867429Seric addheader("Subject", msg, ee); 539*68228Seric p = "return-receipt"; 54067429Seric } 54167261Seric else 54267261Seric { 54367261Seric sprintf(buf, "Returned mail: %.*s", sizeof buf - 20, msg); 54467546Seric addheader("Subject", buf, &ee->e_header); 545*68228Seric p = "failure"; 54667261Seric } 547*68228Seric (void) sprintf(buf, "auto-generated (%s)", p); 548*68228Seric addheader("Auto-Submitted", buf, &ee->e_header); 5494633Seric 5504633Seric /* fake up an address header for the from person */ 55158050Seric expand("\201n", buf, &buf[sizeof buf - 1], e); 55264284Seric if (parseaddr(buf, &ee->e_from, RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL) 5534633Seric { 55458151Seric syserr("553 Can't parse myself!"); 5554633Seric ExitStat = EX_SOFTWARE; 5567045Seric returndepth--; 5574633Seric return (-1); 5584633Seric } 55958704Seric ee->e_sender = ee->e_from.q_paddr; 5605984Seric 5616978Seric /* push state into submessage */ 5626978Seric CurEnv = ee; 56358050Seric define('f', "\201n", ee); 5649375Seric define('x', "Mail Delivery Subsystem", ee); 56558929Seric eatheader(ee, TRUE); 5665984Seric 56763753Seric /* mark statistics */ 56864284Seric markstats(ee, NULLADDR); 56963753Seric 5706978Seric /* actually deliver the error message */ 57114876Seric sendall(ee, SM_DEFAULT); 5726978Seric 5736978Seric /* restore state */ 5747811Seric dropenvelope(ee); 57558680Seric CurEnv = oldcur; 5767045Seric returndepth--; 5776978Seric 5787045Seric /* should check for delivery errors here */ 5794633Seric return (0); 5804633Seric } 5814633Seric /* 5826978Seric ** ERRBODY -- output the body of an error message. 5836978Seric ** 5846978Seric ** Typically this is a copy of the transcript plus a copy of the 5856978Seric ** original offending message. 5866978Seric ** 587297Seric ** Parameters: 58865870Seric ** mci -- the mailer connection information. 5899542Seric ** e -- the envelope we are working in. 59067546Seric ** separator -- any possible MIME separator. 59167936Seric ** flags -- to modify the behaviour. 592297Seric ** 593297Seric ** Returns: 594297Seric ** none 595297Seric ** 596297Seric ** Side Effects: 5976978Seric ** Outputs the body of an error message. 598297Seric */ 599297Seric 600*68228Seric errbody(mci, e, separator) 60165870Seric register MCI *mci; 6029542Seric register ENVELOPE *e; 60367546Seric char *separator; 604297Seric { 6056978Seric register FILE *xfile; 60659082Seric char *p; 60759082Seric register ADDRESS *q; 60859082Seric bool printheader; 6093189Seric char buf[MAXLINE]; 610*68228Seric extern char *xtextify(); 611297Seric 61267546Seric if (bitset(MCIF_INHEADER, mci->mci_flags)) 61367546Seric { 61467546Seric putline("", mci); 61567546Seric mci->mci_flags &= ~MCIF_INHEADER; 61667546Seric } 61758680Seric if (e->e_parent == NULL) 61858680Seric { 61958680Seric syserr("errbody: null parent"); 62065870Seric putline(" ----- Original message lost -----\n", mci); 62158680Seric return; 62258680Seric } 62358680Seric 6249057Seric /* 62559730Seric ** Output MIME header. 62659730Seric */ 62759730Seric 62859730Seric if (e->e_msgboundary != NULL) 62959730Seric { 63065870Seric putline("This is a MIME-encapsulated message", mci); 63165870Seric putline("", mci); 63259730Seric (void) sprintf(buf, "--%s", e->e_msgboundary); 63365870Seric putline(buf, mci); 63465870Seric putline("", mci); 63559730Seric } 63659730Seric 63759730Seric /* 63863852Seric ** Output introductory information. 63963852Seric */ 64063852Seric 64164718Seric for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 64264718Seric if (bitset(QBADADDR, q->q_flags)) 64364718Seric break; 64465054Seric if (q == NULL && 64565054Seric !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags)) 64664718Seric { 64764718Seric putline(" **********************************************", 64865870Seric mci); 64964718Seric putline(" ** THIS IS A WARNING MESSAGE ONLY **", 65065870Seric mci); 65164718Seric putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **", 65265870Seric mci); 65364718Seric putline(" **********************************************", 65465870Seric mci); 65565870Seric putline("", mci); 65664718Seric } 65764718Seric sprintf(buf, "The original message was received at %s", 65864718Seric arpadate(ctime(&e->e_parent->e_ctime))); 65965870Seric putline(buf, mci); 66064025Seric expand("from \201_", buf, &buf[sizeof buf - 1], e->e_parent); 66165870Seric putline(buf, mci); 66265870Seric putline("", mci); 66363852Seric 66463852Seric /* 66555372Seric ** Output error message header (if specified and available). 66655372Seric */ 66755372Seric 66867682Seric if (ErrMsgFile != NULL && !bitset(EF_SENDRECEIPT, e->e_parent->e_flags)) 66955372Seric { 67055372Seric if (*ErrMsgFile == '/') 67155372Seric { 67255372Seric xfile = fopen(ErrMsgFile, "r"); 67355372Seric if (xfile != NULL) 67455372Seric { 67555372Seric while (fgets(buf, sizeof buf, xfile) != NULL) 67655425Seric { 67755425Seric expand(buf, buf, &buf[sizeof buf - 1], e); 67865870Seric putline(buf, mci); 67955425Seric } 68055372Seric (void) fclose(xfile); 68165870Seric putline("\n", mci); 68255372Seric } 68355372Seric } 68455372Seric else 68555372Seric { 68655425Seric expand(ErrMsgFile, buf, &buf[sizeof buf - 1], e); 68765870Seric putline(buf, mci); 68865870Seric putline("", mci); 68955372Seric } 69055372Seric } 69155372Seric 69255372Seric /* 69359082Seric ** Output message introduction 69459082Seric */ 69559082Seric 69659082Seric printheader = TRUE; 69759082Seric for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 69859082Seric { 69967981Seric if (bitset(QBADADDR|QREPORT|QRELAYED, q->q_flags)) 70059082Seric { 70159082Seric if (printheader) 70259082Seric { 70367880Seric putline(" ----- The following addresses have delivery notifications -----", 70465870Seric mci); 70559082Seric printheader = FALSE; 70659082Seric } 70763849Seric strcpy(buf, q->q_paddr); 70863787Seric if (bitset(QBADADDR, q->q_flags)) 70963849Seric strcat(buf, " (unrecoverable error)"); 71067981Seric else if (bitset(QRELAYED, q->q_flags)) 71167981Seric strcat(buf, " (relayed to non-DSN-aware mailer)"); 71267880Seric else if (bitset(QSENT, q->q_flags)) 71367880Seric strcat(buf, " (successfully delivered)"); 71463787Seric else 71563849Seric strcat(buf, " (transient failure)"); 71665870Seric putline(buf, mci); 71763849Seric if (q->q_alias != NULL) 71863849Seric { 71963849Seric strcpy(buf, " (expanded from: "); 72063849Seric strcat(buf, q->q_alias->q_paddr); 72163849Seric strcat(buf, ")"); 72265870Seric putline(buf, mci); 72363849Seric } 72459082Seric } 72559082Seric } 72659082Seric if (!printheader) 72765870Seric putline("\n", mci); 72859082Seric 72959082Seric /* 7309057Seric ** Output transcript of errors 7319057Seric */ 7329057Seric 7334086Seric (void) fflush(stdout); 7349542Seric p = queuename(e->e_parent, 'x'); 7359337Seric if ((xfile = fopen(p, "r")) == NULL) 7369057Seric { 7379337Seric syserr("Cannot open %s", p); 73865870Seric putline(" ----- Transcript of session is unavailable -----\n", mci); 7399057Seric } 7409057Seric else 7419057Seric { 74265870Seric putline(" ----- Transcript of session follows -----\n", mci); 7439542Seric if (e->e_xfp != NULL) 7449542Seric (void) fflush(e->e_xfp); 7459057Seric while (fgets(buf, sizeof buf, xfile) != NULL) 74665870Seric putline(buf, mci); 74758680Seric (void) xfclose(xfile, "errbody xscript", p); 7489057Seric } 749297Seric errno = 0; 7504318Seric 75168028Seric #ifdef DSN 7524318Seric /* 75367880Seric ** Output machine-readable version. 75467880Seric */ 75567880Seric 75667880Seric if (e->e_msgboundary != NULL) 75767880Seric { 75867880Seric putline("", mci); 75967880Seric (void) sprintf(buf, "--%s", e->e_msgboundary); 76067880Seric putline(buf, mci); 761*68228Seric putline("Content-Type: message/X-delivery-status-2 (Draft of 20 January 1995)", mci); 76267880Seric putline("", mci); 76367880Seric 76467880Seric /* 76567880Seric ** Output per-message information. 76667880Seric */ 76767880Seric 76867880Seric /* original envelope id from MAIL FROM: line */ 76967880Seric if (e->e_parent->e_envid != NULL) 77067880Seric { 77167880Seric (void) sprintf(buf, "Original-Envelope-Id: %s", 772*68228Seric xtextify(e->e_parent->e_envid)); 77367880Seric putline(buf, mci); 77467880Seric } 77567880Seric 776*68228Seric /* Reporting-MTA: is us (required) */ 777*68228Seric p = e->e_parent->e_from.q_mailer->m_mtatype; 778*68228Seric if (p == NULL) 779*68228Seric p = "dns"; 780*68228Seric (void) sprintf(buf, "Reporting-MTA: %s; %s", p, MyHostName); 78167880Seric putline(buf, mci); 78267880Seric 783*68228Seric /* Received-From-MTA: shows where we got this message from */ 78467990Seric if (RealHostName != NULL) 78567990Seric { 786*68228Seric /* XXX use $s for type? */ 787*68228Seric p = e->e_parent->e_from.q_mailer->m_mtatype; 788*68228Seric if (p == NULL) 789*68228Seric p = "dns"; 790*68228Seric (void) sprintf(buf, "Received-From-MTA: %s; %s", 791*68228Seric p, RealHostName); 79267990Seric putline(buf, mci); 79367990Seric } 79467963Seric 79567963Seric /* Arrival-Date: -- when it arrived here */ 79667963Seric (void) sprintf(buf, "Arrival-Date: %s", 79767963Seric arpadate(ctime(&e->e_parent->e_ctime))); 79867963Seric putline(buf, mci); 79967963Seric 80067880Seric /* 80167880Seric ** Output per-address information. 80267880Seric */ 80367880Seric 80467880Seric for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 80567880Seric { 80667880Seric register ADDRESS *r; 80767880Seric 80867880Seric if (!bitset(QBADADDR|QREPORT|QRELAYED, q->q_flags)) 80967880Seric continue; 81067880Seric putline("", mci); 81167880Seric 812*68228Seric /* Original-Recipient: -- passed from on high */ 813*68228Seric if (q->q_orcpt != NULL) 814*68228Seric { 815*68228Seric (void) sprintf(buf, "Original-Recipient: %s", 816*68228Seric xtextify(q->q_orcpt)); 817*68228Seric putline(buf, mci); 818*68228Seric } 819*68228Seric 820*68228Seric /* Final-Recipient: -- the name from the RCPT command */ 821*68228Seric p = e->e_parent->e_from.q_mailer->m_addrtype; 822*68228Seric if (p == NULL) 823*68228Seric p = "rfc822"; 824*68228Seric for (r = q; r->q_alias != NULL; r = r->q_alias) 825*68228Seric continue; 826*68228Seric if (strchr(r->q_user, '@') == NULL) 827*68228Seric (void) sprintf(buf, "Final-Recipient: %s; %s@%s", 828*68228Seric p, xtextify(r->q_user), MyHostName); 82967998Seric else 830*68228Seric (void) sprintf(buf, "Final-Recipient: %s; %s", 831*68228Seric p, xtextify(r->q_user)); 83267880Seric putline(buf, mci); 83367880Seric 83467880Seric /* Action: -- what happened? */ 83567880Seric if (bitset(QBADADDR, q->q_flags)) 836*68228Seric putline("Action: failure", mci); 83767880Seric else if (bitset(QQUEUEUP, q->q_flags)) 83867880Seric putline("Action: delayed", mci); 83967880Seric else if (bitset(QRELAYED, q->q_flags)) 84067880Seric putline("Action: relayed", mci); 84167880Seric else 84267880Seric putline("Action: delivered", mci); 84367880Seric 84467880Seric /* Status: -- what _really_ happened? */ 84567880Seric strcpy(buf, "Status: "); 84667880Seric if (q->q_status != NULL) 84767880Seric strcat(buf, q->q_status); 84867880Seric else if (bitset(QBADADDR, q->q_flags)) 849*68228Seric strcat(buf, "5.0.0"); 85067880Seric else if (bitset(QQUEUEUP, q->q_flags)) 851*68228Seric strcat(buf, "4.0.0"); 85267880Seric else if (bitset(QRELAYED, q->q_flags)) 853*68228Seric strcat(buf, "6.0.1"); 85467880Seric else 855*68228Seric strcat(buf, "2.0.0"); 85667880Seric putline(buf, mci); 85767880Seric 858*68228Seric /* Remote-MTA: -- who was I talking to? */ 859*68228Seric p = q->q_mailer->m_mtatype; 860*68228Seric if (p == NULL) 861*68228Seric p = "dns"; 862*68228Seric (void) sprintf(buf, "Remote-MTA: %s; ", p); 863*68228Seric if (q->q_statmta != NULL) 864*68228Seric p = q->q_statmta; 865*68228Seric else if (q->q_host != NULL) 866*68228Seric p = q->q_host; 867*68228Seric else 868*68228Seric p = NULL; 869*68228Seric if (p != NULL) 870*68228Seric { 871*68228Seric strcat(buf, p); 872*68228Seric p = &buf[strlen(buf) - 1]; 873*68228Seric if (*p == '.') 874*68228Seric *p = '\0'; 875*68228Seric putline(buf, mci); 876*68228Seric } 877*68228Seric 878*68228Seric /* Diagnostic-Code: -- actual result from other end */ 879*68228Seric if (q->q_rstatus != NULL) 880*68228Seric { 881*68228Seric p = q->q_mailer->m_diagtype; 882*68228Seric if (p == NULL) 883*68228Seric p = "smtp"; 884*68228Seric (void) sprintf(buf, "Diagnostic-Code: %s; %s", 885*68228Seric p, q->q_rstatus); 886*68228Seric putline(buf, mci); 887*68228Seric } 888*68228Seric 889*68228Seric /* Last-Attempt-Date: -- fine granularity */ 89067880Seric if (q->q_statdate == (time_t) 0L) 89167880Seric q->q_statdate = curtime(); 892*68228Seric (void) sprintf(buf, "Last-Attempt-Date: %s", 89367880Seric arpadate(ctime(&q->q_statdate))); 89467880Seric putline(buf, mci); 89567880Seric 89667963Seric /* Expiry-Date: -- for delayed messages only */ 89767963Seric if (bitset(QQUEUEUP, q->q_flags) && 89867963Seric !bitset(QBADADDR, q->q_flags)) 89967963Seric { 90067963Seric time_t xdate; 90167963Seric 90267963Seric xdate = e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass]; 90367963Seric sprintf(buf, "Expiry-Date: %s", 90467963Seric arpadate(ctime(&xdate))); 90567963Seric putline(buf, mci); 90667963Seric } 90767880Seric } 90867880Seric } 90968028Seric #endif 91067880Seric 91167880Seric /* 9124318Seric ** Output text of original message 9134318Seric */ 9144318Seric 91567880Seric if (bitset(EF_NORETURN, e->e_parent->e_flags)) 91658665Seric SendBody = FALSE; 91765870Seric putline("", mci); 91858680Seric if (e->e_parent->e_df != NULL) 9194199Seric { 92067880Seric if (e->e_msgboundary == NULL) 92167880Seric { 92267880Seric if (SendBody) 92367880Seric putline(" ----- Original message follows -----\n", mci); 92467880Seric else 92567880Seric putline(" ----- Message header follows -----\n", mci); 92667880Seric (void) fflush(mci->mci_out); 92767880Seric } 9285984Seric else 9295984Seric { 93059730Seric (void) sprintf(buf, "--%s", e->e_msgboundary); 93165870Seric putline(buf, mci); 932*68228Seric (void) sprintf(buf, "Content-Type: message/rfc822%s", 933*68228Seric mci, SendBody ? "" : "-headers"); 934*68228Seric putline(buf, mci); 9355984Seric } 93667981Seric putline("", mci); 937*68228Seric putheader(mci, e->e_parent->e_header, e->e_parent); 93859730Seric if (SendBody) 939*68228Seric putbody(mci, e->e_parent, e->e_msgboundary); 940*68228Seric else if (e->e_msgboundary == NULL) 94167546Seric { 94267546Seric putline("", mci); 94365870Seric putline(" ----- Message body suppressed -----", mci); 94467546Seric } 9454199Seric } 946*68228Seric else if (e->e_msgboundary == NULL) 94710170Seric { 94865870Seric putline(" ----- No message was collected -----\n", mci); 94910170Seric } 9504318Seric 95160010Seric if (e->e_msgboundary != NULL) 95260010Seric { 95365870Seric putline("", mci); 95460010Seric (void) sprintf(buf, "--%s--", e->e_msgboundary); 95565870Seric putline(buf, mci); 95660010Seric } 95765870Seric putline("", mci); 95859730Seric 9594318Seric /* 9604318Seric ** Cleanup and exit 9614318Seric */ 9624318Seric 963297Seric if (errno != 0) 9646978Seric syserr("errbody: I/O error"); 965297Seric } 96658144Seric /* 967*68228Seric ** SMTPTODSN -- convert SMTP to DSN status code 968*68228Seric ** 969*68228Seric ** Parameters: 970*68228Seric ** smtpstat -- the smtp status code (e.g., 550). 971*68228Seric ** 972*68228Seric ** Returns: 973*68228Seric ** The DSN version of the status code. 974*68228Seric */ 975*68228Seric 976*68228Seric char * 977*68228Seric smtptodsn(smtpstat) 978*68228Seric int smtpstat; 979*68228Seric { 980*68228Seric switch (smtpstat) 981*68228Seric { 982*68228Seric case 450: /* Req mail action not taken: mailbox unavailable */ 983*68228Seric return "4.2.0"; 984*68228Seric 985*68228Seric case 451: /* Req action aborted: local error in processing */ 986*68228Seric return "4.3.0"; 987*68228Seric 988*68228Seric case 452: /* Req action not taken: insufficient sys storage */ 989*68228Seric return "4.3.1"; 990*68228Seric 991*68228Seric case 500: /* Syntax error, command unrecognized */ 992*68228Seric return "5.5.2"; 993*68228Seric 994*68228Seric case 501: /* Syntax error in parameters or arguments */ 995*68228Seric return "5.5.4"; 996*68228Seric 997*68228Seric case 502: /* Command not implemented */ 998*68228Seric return "5.5.1"; 999*68228Seric 1000*68228Seric case 503: /* Bad sequence of commands */ 1001*68228Seric return "5.5.1"; 1002*68228Seric 1003*68228Seric case 504: /* Command parameter not implemented */ 1004*68228Seric return "5.5.4"; 1005*68228Seric 1006*68228Seric case 550: /* Req mail action not taken: mailbox unavailable */ 1007*68228Seric return "5.2.0"; 1008*68228Seric 1009*68228Seric case 551: /* User not local; please try <...> */ 1010*68228Seric return "5.1.6"; 1011*68228Seric 1012*68228Seric case 552: /* Req mail action aborted: exceeded storage alloc */ 1013*68228Seric return "5.2.2"; 1014*68228Seric 1015*68228Seric case 553: /* Req action not taken: mailbox name not allowed */ 1016*68228Seric return "5.1.3"; 1017*68228Seric 1018*68228Seric case 554: /* Transaction failed */ 1019*68228Seric return "5.0.0"; 1020*68228Seric } 1021*68228Seric 1022*68228Seric if ((smtpstat / 100) == 2) 1023*68228Seric return "2.0.0"; 1024*68228Seric if ((smtpstat / 100) == 4) 1025*68228Seric return "4.0.0"; 1026*68228Seric return "5.0.0"; 1027*68228Seric } 1028*68228Seric /* 1029*68228Seric ** XTEXTIFY -- take regular text and turn it into DSN-style xtext 1030*68228Seric ** 1031*68228Seric ** Parameters: 1032*68228Seric ** t -- the text to convert. 1033*68228Seric ** 1034*68228Seric ** Returns: 1035*68228Seric ** The xtext-ified version of the same string. 1036*68228Seric */ 1037*68228Seric 1038*68228Seric char * 1039*68228Seric xtextify(t) 1040*68228Seric register char *t; 1041*68228Seric { 1042*68228Seric register char *p; 1043*68228Seric int l; 1044*68228Seric int nbogus; 1045*68228Seric static char *bp = NULL; 1046*68228Seric static int bplen = 0; 1047*68228Seric 1048*68228Seric /* figure out how long this xtext will have to be */ 1049*68228Seric nbogus = l = 0; 1050*68228Seric for (p = t; *p != '\0'; p++) 1051*68228Seric { 1052*68228Seric register int c = (*p & 0xff); 1053*68228Seric 1054*68228Seric /* ASCII dependence here -- this is the way the spec words it */ 1055*68228Seric if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(') 1056*68228Seric nbogus++; 1057*68228Seric l++; 1058*68228Seric } 1059*68228Seric if (nbogus == 0) 1060*68228Seric return t; 1061*68228Seric l += nbogus * 2 + 1; 1062*68228Seric 1063*68228Seric /* now allocate space if necessary for the new string */ 1064*68228Seric if (l > bplen) 1065*68228Seric { 1066*68228Seric if (bp != NULL) 1067*68228Seric free(bp); 1068*68228Seric bp = xalloc(l); 1069*68228Seric bplen = l; 1070*68228Seric } 1071*68228Seric 1072*68228Seric /* ok, copy the text with byte expansion */ 1073*68228Seric for (p = bp; *t != '\0'; ) 1074*68228Seric { 1075*68228Seric register int c = (*t++ & 0xff); 1076*68228Seric 1077*68228Seric /* ASCII dependence here -- this is the way the spec words it */ 1078*68228Seric if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(') 1079*68228Seric { 1080*68228Seric *p++ = '+'; 1081*68228Seric *p++ = "0123456789abcdef"[c >> 4]; 1082*68228Seric *p++ = "0123456789abcdef"[c & 0xf]; 1083*68228Seric } 1084*68228Seric else 1085*68228Seric *p++ = c; 1086*68228Seric } 1087*68228Seric *p = '\0'; 1088*68228Seric return bp; 1089*68228Seric } 1090*68228Seric /* 109158144Seric ** PRUNEROUTE -- prune an RFC-822 source route 109258144Seric ** 109358144Seric ** Trims down a source route to the last internet-registered hop. 109458144Seric ** This is encouraged by RFC 1123 section 5.3.3. 109558144Seric ** 109658144Seric ** Parameters: 109758144Seric ** addr -- the address 109858144Seric ** 109958144Seric ** Returns: 110058144Seric ** TRUE -- address was modified 110158144Seric ** FALSE -- address could not be pruned 110258144Seric ** 110358144Seric ** Side Effects: 110458144Seric ** modifies addr in-place 110558144Seric */ 110658144Seric 110758144Seric pruneroute(addr) 110858144Seric char *addr; 110958144Seric { 111066334Seric #if NAMED_BIND 111158144Seric char *start, *at, *comma; 111258144Seric char c; 111358144Seric int rcode; 111458144Seric char hostbuf[BUFSIZ]; 111558144Seric char *mxhosts[MAXMXHOSTS + 1]; 111658144Seric 111758144Seric /* check to see if this is really a route-addr */ 111858144Seric if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') 111958144Seric return FALSE; 112058144Seric start = strchr(addr, ':'); 112158144Seric at = strrchr(addr, '@'); 112258144Seric if (start == NULL || at == NULL || at < start) 112358144Seric return FALSE; 112458144Seric 112558144Seric /* slice off the angle brackets */ 112658144Seric strcpy(hostbuf, at + 1); 112758144Seric hostbuf[strlen(hostbuf) - 1] = '\0'; 112858144Seric 112958144Seric while (start) 113058144Seric { 113159273Seric if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0) 113258144Seric { 113358144Seric strcpy(addr + 1, start + 1); 113458144Seric return TRUE; 113558144Seric } 113658144Seric c = *start; 113758144Seric *start = '\0'; 113858144Seric comma = strrchr(addr, ','); 113958144Seric if (comma && comma[1] == '@') 114058144Seric strcpy(hostbuf, comma + 2); 114158144Seric else 114258144Seric comma = 0; 114358144Seric *start = c; 114458144Seric start = comma; 114558144Seric } 114658144Seric #endif 114758144Seric return FALSE; 114858144Seric } 1149