savemail.c revision 66497
138032Speter/* 264565Sgshapiro * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. 364565Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1438032Speter#ifndef lint 1566497Sgshapirostatic char id[] = "@(#)$Id: savemail.c,v 8.212.4.5 2000/08/22 22:46:00 gshapiro Exp $"; 1664565Sgshapiro#endif /* ! lint */ 1738032Speter 1864565Sgshapiro/* $FreeBSD: head/contrib/sendmail/src/savemail.c 66497 2000-10-01 02:03:50Z gshapiro $ */ 1938032Speter 2064565Sgshapiro#include <sendmail.h> 2164565Sgshapiro 2264565Sgshapiro 2364565Sgshapirostatic void errbody __P((MCI *, ENVELOPE *, char *)); 2464565Sgshapirostatic bool pruneroute __P((char *)); 2564565Sgshapiro 2638032Speter/* 2738032Speter** SAVEMAIL -- Save mail on error 2838032Speter** 2938032Speter** If mailing back errors, mail it back to the originator 3038032Speter** together with an error message; otherwise, just put it in 3138032Speter** dead.letter in the user's home directory (if he exists on 3238032Speter** this machine). 3338032Speter** 3438032Speter** Parameters: 3538032Speter** e -- the envelope containing the message in error. 3638032Speter** sendbody -- if TRUE, also send back the body of the 3738032Speter** message; otherwise just send the header. 3838032Speter** 3938032Speter** Returns: 4038032Speter** none 4138032Speter** 4238032Speter** Side Effects: 4338032Speter** Saves the letter, by writing or mailing it back to the 4438032Speter** sender, or by putting it in dead.letter in her home 4538032Speter** directory. 4638032Speter*/ 4738032Speter 4838032Speter/* defines for state machine */ 4964565Sgshapiro#define ESM_REPORT 0 /* report to sender's terminal */ 5064565Sgshapiro#define ESM_MAIL 1 /* mail back to sender */ 5164565Sgshapiro#define ESM_QUIET 2 /* mail has already been returned */ 5264565Sgshapiro#define ESM_DEADLETTER 3 /* save in ~/dead.letter */ 5364565Sgshapiro#define ESM_POSTMASTER 4 /* return to postmaster */ 5464565Sgshapiro#define ESM_DEADLETTERDROP 5 /* save in DeadLetterDrop */ 5564565Sgshapiro#define ESM_PANIC 6 /* call loseqfile() */ 5664565Sgshapiro#define ESM_DONE 7 /* message is successfully delivered */ 5738032Speter 5838032Speter 5938032Spetervoid 6038032Spetersavemail(e, sendbody) 6138032Speter register ENVELOPE *e; 6238032Speter bool sendbody; 6338032Speter{ 6438032Speter register struct passwd *pw; 6538032Speter register FILE *fp; 6638032Speter int state; 6738032Speter auto ADDRESS *q = NULL; 6838032Speter register char *p; 6938032Speter MCI mcibuf; 7038032Speter int flags; 7164565Sgshapiro long sff; 7264565Sgshapiro char buf[MAXLINE + 1]; 7338032Speter 7438032Speter if (tTd(6, 1)) 7538032Speter { 7664565Sgshapiro dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=", 7738032Speter e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id, 7838032Speter ExitStat); 7938032Speter printaddr(&e->e_from, FALSE); 8038032Speter } 8138032Speter 8238032Speter if (e->e_id == NULL) 8338032Speter { 8438032Speter /* can't return a message with no id */ 8538032Speter return; 8638032Speter } 8738032Speter 8838032Speter /* 8938032Speter ** In the unhappy event we don't know who to return the mail 9038032Speter ** to, make someone up. 9138032Speter */ 9238032Speter 9338032Speter if (e->e_from.q_paddr == NULL) 9438032Speter { 9538032Speter e->e_sender = "Postmaster"; 9638032Speter if (parseaddr(e->e_sender, &e->e_from, 9738032Speter RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL) 9838032Speter { 9964565Sgshapiro syserr("553 5.3.5 Cannot parse Postmaster!"); 10042580Speter finis(TRUE, EX_SOFTWARE); 10138032Speter } 10238032Speter } 10338032Speter e->e_to = NULL; 10438032Speter 10538032Speter /* 10638032Speter ** Basic state machine. 10738032Speter ** 10838032Speter ** This machine runs through the following states: 10938032Speter ** 11038032Speter ** ESM_QUIET Errors have already been printed iff the 11138032Speter ** sender is local. 11238032Speter ** ESM_REPORT Report directly to the sender's terminal. 11338032Speter ** ESM_MAIL Mail response to the sender. 11438032Speter ** ESM_DEADLETTER Save response in ~/dead.letter. 11538032Speter ** ESM_POSTMASTER Mail response to the postmaster. 11664565Sgshapiro ** ESM_DEADLETTERDROP 11764565Sgshapiro ** If DeadLetterDrop set, save it there. 11838032Speter ** ESM_PANIC Save response anywhere possible. 11938032Speter */ 12038032Speter 12138032Speter /* determine starting state */ 12238032Speter switch (e->e_errormode) 12338032Speter { 12438032Speter case EM_WRITE: 12538032Speter state = ESM_REPORT; 12638032Speter break; 12738032Speter 12838032Speter case EM_BERKNET: 12938032Speter case EM_MAIL: 13038032Speter state = ESM_MAIL; 13138032Speter break; 13238032Speter 13338032Speter case EM_PRINT: 13438032Speter case '\0': 13538032Speter state = ESM_QUIET; 13638032Speter break; 13738032Speter 13838032Speter case EM_QUIET: 13938032Speter /* no need to return anything at all */ 14038032Speter return; 14138032Speter 14238032Speter default: 14364565Sgshapiro syserr("554 5.3.0 savemail: bogus errormode x%x\n", 14464565Sgshapiro e->e_errormode); 14538032Speter state = ESM_MAIL; 14638032Speter break; 14738032Speter } 14838032Speter 14938032Speter /* if this is already an error response, send to postmaster */ 15038032Speter if (bitset(EF_RESPONSE, e->e_flags)) 15138032Speter { 15238032Speter if (e->e_parent != NULL && 15338032Speter bitset(EF_RESPONSE, e->e_parent->e_flags)) 15438032Speter { 15538032Speter /* got an error sending a response -- can it */ 15638032Speter return; 15738032Speter } 15838032Speter state = ESM_POSTMASTER; 15938032Speter } 16038032Speter 16138032Speter while (state != ESM_DONE) 16238032Speter { 16338032Speter if (tTd(6, 5)) 16464565Sgshapiro dprintf(" state %d\n", state); 16538032Speter 16638032Speter switch (state) 16738032Speter { 16838032Speter case ESM_QUIET: 16938032Speter if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags)) 17038032Speter state = ESM_DEADLETTER; 17138032Speter else 17238032Speter state = ESM_MAIL; 17338032Speter break; 17438032Speter 17538032Speter case ESM_REPORT: 17638032Speter 17738032Speter /* 17838032Speter ** If the user is still logged in on the same terminal, 17938032Speter ** then write the error messages back to hir (sic). 18038032Speter */ 18138032Speter 18238032Speter p = ttypath(); 18338032Speter if (p == NULL || freopen(p, "w", stdout) == NULL) 18438032Speter { 18538032Speter state = ESM_MAIL; 18638032Speter break; 18738032Speter } 18838032Speter 18938032Speter expand("\201n", buf, sizeof buf, e); 19038032Speter printf("\r\nMessage from %s...\r\n", buf); 19138032Speter printf("Errors occurred while sending mail.\r\n"); 19238032Speter if (e->e_xfp != NULL) 19338032Speter { 19464565Sgshapiro (void) bfrewind(e->e_xfp); 19564565Sgshapiro printf("Transcript follows:\r\n"); 19664565Sgshapiro while (fgets(buf, sizeof buf, e->e_xfp) != NULL && 19764565Sgshapiro !ferror(stdout)) 19864565Sgshapiro (void) fputs(buf, stdout); 19938032Speter } 20038032Speter else 20138032Speter { 20238032Speter syserr("Cannot open %s", queuename(e, 'x')); 20338032Speter printf("Transcript of session is unavailable.\r\n"); 20438032Speter } 20538032Speter printf("Original message will be saved in dead.letter.\r\n"); 20638032Speter state = ESM_DEADLETTER; 20738032Speter break; 20838032Speter 20938032Speter case ESM_MAIL: 21038032Speter /* 21138032Speter ** If mailing back, do it. 21238032Speter ** Throw away all further output. Don't alias, 21338032Speter ** since this could cause loops, e.g., if joe 21438032Speter ** mails to joe@x, and for some reason the network 21538032Speter ** for @x is down, then the response gets sent to 21638032Speter ** joe@x, which gives a response, etc. Also force 21738032Speter ** the mail to be delivered even if a version of 21838032Speter ** it has already been sent to the sender. 21938032Speter ** 22038032Speter ** If this is a configuration or local software 22138032Speter ** error, send to the local postmaster as well, 22238032Speter ** since the originator can't do anything 22338032Speter ** about it anyway. Note that this is a full 22438032Speter ** copy of the message (intentionally) so that 22538032Speter ** the Postmaster can forward things along. 22638032Speter */ 22738032Speter 22838032Speter if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE) 22938032Speter { 23038032Speter (void) sendtolist("postmaster", 23138032Speter NULLADDR, &e->e_errorqueue, 0, e); 23238032Speter } 23338032Speter if (!emptyaddr(&e->e_from)) 23438032Speter { 23538032Speter char from[TOBUFSIZE]; 23638032Speter 23764565Sgshapiro if (strlen(e->e_from.q_paddr) >= sizeof from) 23838032Speter { 23938032Speter state = ESM_POSTMASTER; 24038032Speter break; 24138032Speter } 24264565Sgshapiro (void) strlcpy(from, e->e_from.q_paddr, 24364565Sgshapiro sizeof from); 24438032Speter 24538032Speter if (!DontPruneRoutes && pruneroute(from)) 24638032Speter { 24738032Speter ADDRESS *a; 24838032Speter 24938032Speter for (a = e->e_errorqueue; a != NULL; 25038032Speter a = a->q_next) 25138032Speter { 25238032Speter if (sameaddr(a, &e->e_from)) 25364565Sgshapiro a->q_state = QS_DUPLICATE; 25438032Speter } 25538032Speter } 25638032Speter (void) sendtolist(from, NULLADDR, 25738032Speter &e->e_errorqueue, 0, e); 25838032Speter } 25938032Speter 26038032Speter /* 26138032Speter ** Deliver a non-delivery report to the 26238032Speter ** Postmaster-designate (not necessarily 26338032Speter ** Postmaster). This does not include the 26438032Speter ** body of the message, for privacy reasons. 26538032Speter ** You really shouldn't need this. 26638032Speter */ 26738032Speter 26838032Speter e->e_flags |= EF_PM_NOTIFY; 26938032Speter 27038032Speter /* check to see if there are any good addresses */ 27138032Speter for (q = e->e_errorqueue; q != NULL; q = q->q_next) 27264565Sgshapiro { 27364565Sgshapiro if (QS_IS_SENDABLE(q->q_state)) 27438032Speter break; 27564565Sgshapiro } 27638032Speter if (q == NULL) 27738032Speter { 27838032Speter /* this is an error-error */ 27938032Speter state = ESM_POSTMASTER; 28038032Speter break; 28138032Speter } 28238032Speter if (returntosender(e->e_message, e->e_errorqueue, 28338032Speter sendbody ? RTSF_SEND_BODY 28438032Speter : RTSF_NO_BODY, 28538032Speter e) == 0) 28638032Speter { 28738032Speter state = ESM_DONE; 28838032Speter break; 28938032Speter } 29038032Speter 29138032Speter /* didn't work -- return to postmaster */ 29238032Speter state = ESM_POSTMASTER; 29338032Speter break; 29438032Speter 29538032Speter case ESM_POSTMASTER: 29638032Speter /* 29738032Speter ** Similar to previous case, but to system postmaster. 29838032Speter */ 29938032Speter 30038032Speter q = NULL; 30164565Sgshapiro expand(DoubleBounceAddr, buf, sizeof buf, e); 30264565Sgshapiro if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0) 30338032Speter { 30464565Sgshapiro syserr("553 5.3.0 cannot parse %s!", buf); 30538032Speter ExitStat = EX_SOFTWARE; 30664565Sgshapiro state = ESM_DEADLETTERDROP; 30738032Speter break; 30838032Speter } 30938032Speter flags = RTSF_PM_BOUNCE; 31038032Speter if (sendbody) 31138032Speter flags |= RTSF_SEND_BODY; 31238032Speter if (returntosender(e->e_message, q, flags, e) == 0) 31338032Speter { 31438032Speter state = ESM_DONE; 31538032Speter break; 31638032Speter } 31738032Speter 31838032Speter /* didn't work -- last resort */ 31964565Sgshapiro state = ESM_DEADLETTERDROP; 32038032Speter break; 32138032Speter 32238032Speter case ESM_DEADLETTER: 32338032Speter /* 32438032Speter ** Save the message in dead.letter. 32538032Speter ** If we weren't mailing back, and the user is 32638032Speter ** local, we should save the message in 32738032Speter ** ~/dead.letter so that the poor person doesn't 32838032Speter ** have to type it over again -- and we all know 32938032Speter ** what poor typists UNIX users are. 33038032Speter */ 33138032Speter 33238032Speter p = NULL; 33338032Speter if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags)) 33438032Speter { 33538032Speter if (e->e_from.q_home != NULL) 33638032Speter p = e->e_from.q_home; 33766497Sgshapiro else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL && 33866497Sgshapiro *pw->pw_dir != '\0') 33938032Speter p = pw->pw_dir; 34038032Speter } 34138032Speter if (p == NULL || e->e_dfp == NULL) 34238032Speter { 34338032Speter /* no local directory or no data file */ 34438032Speter state = ESM_MAIL; 34538032Speter break; 34638032Speter } 34738032Speter 34838032Speter /* we have a home directory; write dead.letter */ 34938032Speter define('z', p, e); 35064565Sgshapiro 35164565Sgshapiro /* get the sender for the UnixFromLine */ 35264565Sgshapiro p = macvalue('g', e); 35364565Sgshapiro define('g', e->e_sender, e); 35464565Sgshapiro 35538032Speter expand("\201z/dead.letter", buf, sizeof buf, e); 35664565Sgshapiro sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; 35738032Speter if (RealUid == 0) 35864565Sgshapiro sff |= SFF_ROOTOK; 35938032Speter e->e_to = buf; 36064565Sgshapiro if (writable(buf, NULL, sff) && 36164565Sgshapiro mailfile(buf, FileMailer, NULL, sff, e) == EX_OK) 36238032Speter { 36338032Speter int oldverb = Verbose; 36438032Speter 36564565Sgshapiro if (OpMode != MD_DAEMON && OpMode != MD_SMTP) 36664565Sgshapiro Verbose = 1; 36764565Sgshapiro if (Verbose > 0) 36864565Sgshapiro message("Saved message in %s", buf); 36938032Speter Verbose = oldverb; 37064565Sgshapiro define('g', p, e); 37138032Speter state = ESM_DONE; 37238032Speter break; 37338032Speter } 37464565Sgshapiro define('g', p, e); 37538032Speter state = ESM_MAIL; 37638032Speter break; 37738032Speter 37864565Sgshapiro case ESM_DEADLETTERDROP: 37938032Speter /* 38064565Sgshapiro ** Log the mail in DeadLetterDrop file. 38138032Speter */ 38238032Speter 38338032Speter if (e->e_class < 0) 38438032Speter { 38538032Speter state = ESM_DONE; 38638032Speter break; 38738032Speter } 38838032Speter 38938032Speter if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') || 39064565Sgshapiro DeadLetterDrop == NULL || 39164565Sgshapiro DeadLetterDrop[0] == '\0') 39238032Speter { 39338032Speter state = ESM_PANIC; 39438032Speter break; 39538032Speter } 39638032Speter 39764565Sgshapiro sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN; 39864565Sgshapiro if (!writable(DeadLetterDrop, NULL, sff) || 39938032Speter (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND, 40064565Sgshapiro FileMode, sff)) == NULL) 40138032Speter { 40238032Speter state = ESM_PANIC; 40338032Speter break; 40438032Speter } 40538032Speter 40664565Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 40738032Speter mcibuf.mci_out = fp; 40838032Speter mcibuf.mci_mailer = FileMailer; 40938032Speter if (bitnset(M_7BITS, FileMailer->m_flags)) 41038032Speter mcibuf.mci_flags |= MCIF_7BIT; 41138032Speter 41264565Sgshapiro /* get the sender for the UnixFromLine */ 41364565Sgshapiro p = macvalue('g', e); 41464565Sgshapiro define('g', e->e_sender, e); 41564565Sgshapiro 41638032Speter putfromline(&mcibuf, e); 41743733Speter (*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER); 41838032Speter (*e->e_putbody)(&mcibuf, e, NULL); 41938032Speter putline("\n", &mcibuf); 42038032Speter (void) fflush(fp); 42164565Sgshapiro if (ferror(fp) || 42264565Sgshapiro fclose(fp) < 0) 42338032Speter state = ESM_PANIC; 42438032Speter else 42538032Speter { 42638032Speter int oldverb = Verbose; 42738032Speter 42864565Sgshapiro if (OpMode != MD_DAEMON && OpMode != MD_SMTP) 42964565Sgshapiro Verbose = 1; 43064565Sgshapiro if (Verbose > 0) 43164565Sgshapiro message("Saved message in %s", 43264565Sgshapiro DeadLetterDrop); 43338032Speter Verbose = oldverb; 43438032Speter if (LogLevel > 3) 43538032Speter sm_syslog(LOG_NOTICE, e->e_id, 43664565Sgshapiro "Saved message in %s", 43764565Sgshapiro DeadLetterDrop); 43838032Speter state = ESM_DONE; 43938032Speter } 44064565Sgshapiro define('g', p, e); 44138032Speter break; 44238032Speter 44338032Speter default: 44464565Sgshapiro syserr("554 5.3.5 savemail: unknown state %d", state); 44538032Speter 44664565Sgshapiro /* FALLTHROUGH */ 44738032Speter 44838032Speter case ESM_PANIC: 44938032Speter /* leave the locked queue & transcript files around */ 45038032Speter loseqfile(e, "savemail panic"); 45166497Sgshapiro errno = 0; 45238032Speter syserr("!554 savemail: cannot save rejected email anywhere"); 45338032Speter } 45438032Speter } 45538032Speter} 45638032Speter/* 45738032Speter** RETURNTOSENDER -- return a message to the sender with an error. 45838032Speter** 45938032Speter** Parameters: 46038032Speter** msg -- the explanatory message. 46138032Speter** returnq -- the queue of people to send the message to. 46238032Speter** flags -- flags tweaking the operation: 46338032Speter** RTSF_SENDBODY -- include body of message (otherwise 46438032Speter** just send the header). 46538032Speter** RTSF_PMBOUNCE -- this is a postmaster bounce. 46638032Speter** e -- the current envelope. 46738032Speter** 46838032Speter** Returns: 46938032Speter** zero -- if everything went ok. 47038032Speter** else -- some error. 47138032Speter** 47238032Speter** Side Effects: 47338032Speter** Returns the current message to the sender via 47438032Speter** mail. 47538032Speter*/ 47638032Speter 47738032Speter#define MAXRETURNS 6 /* max depth of returning messages */ 47838032Speter#define ERRORFUDGE 100 /* nominal size of error message text */ 47938032Speter 48038032Speterint 48138032Speterreturntosender(msg, returnq, flags, e) 48238032Speter char *msg; 48338032Speter ADDRESS *returnq; 48438032Speter int flags; 48538032Speter register ENVELOPE *e; 48638032Speter{ 48738032Speter register ENVELOPE *ee; 48838032Speter ENVELOPE *oldcur = CurEnv; 48938032Speter ENVELOPE errenvelope; 49038032Speter static int returndepth = 0; 49138032Speter register ADDRESS *q; 49238032Speter char *p; 49338032Speter char buf[MAXNAME + 1]; 49438032Speter 49538032Speter if (returnq == NULL) 49664565Sgshapiro return -1; 49738032Speter 49838032Speter if (msg == NULL) 49938032Speter msg = "Unable to deliver mail"; 50038032Speter 50138032Speter if (tTd(6, 1)) 50238032Speter { 50364565Sgshapiro dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%lx, returnq=", 50464565Sgshapiro msg, returndepth, (u_long) e); 50538032Speter printaddr(returnq, TRUE); 50638032Speter if (tTd(6, 20)) 50738032Speter { 50864565Sgshapiro dprintf("Sendq="); 50938032Speter printaddr(e->e_sendqueue, TRUE); 51038032Speter } 51138032Speter } 51238032Speter 51338032Speter if (++returndepth >= MAXRETURNS) 51438032Speter { 51538032Speter if (returndepth != MAXRETURNS) 51664565Sgshapiro syserr("554 5.3.0 returntosender: infinite recursion on %s", 51764565Sgshapiro returnq->q_paddr); 51838032Speter /* don't "unrecurse" and fake a clean exit */ 51938032Speter /* returndepth--; */ 52064565Sgshapiro return 0; 52138032Speter } 52238032Speter 52364565Sgshapiro define('g', e->e_sender, e); 52438032Speter define('u', NULL, e); 52538032Speter 52638032Speter /* initialize error envelope */ 52738032Speter ee = newenvelope(&errenvelope, e); 52838032Speter define('a', "\201b", ee); 52964565Sgshapiro define('r', "", ee); 53038032Speter define('s', "localhost", ee); 53138032Speter define('_', "localhost", ee); 53264565Sgshapiro#if SASL 53364565Sgshapiro define(macid("{auth_type}", NULL), "", ee); 53464565Sgshapiro define(macid("{auth_authen}", NULL), "", ee); 53564565Sgshapiro define(macid("{auth_author}", NULL), "", ee); 53664565Sgshapiro define(macid("{auth_ssf}", NULL), "", ee); 53764565Sgshapiro#endif /* SASL */ 53864565Sgshapiro#if STARTTLS 53964565Sgshapiro define(macid("{cert_issuer}", NULL), "", ee); 54064565Sgshapiro define(macid("{cert_subject}", NULL), "", ee); 54164565Sgshapiro define(macid("{cipher_bits}", NULL), "", ee); 54264565Sgshapiro define(macid("{cipher}", NULL), "", ee); 54364565Sgshapiro define(macid("{tls_version}", NULL), "", ee); 54464565Sgshapiro define(macid("{verify}", NULL), "", ee); 54564565Sgshapiro# if _FFR_TLS_1 54664565Sgshapiro define(macid("{alg_bits}", NULL), "", ee); 54764565Sgshapiro define(macid("{cn_issuer}", NULL), "", ee); 54864565Sgshapiro define(macid("{cn_subject}", NULL), "", ee); 54964565Sgshapiro# endif /* _FFR_TLS_1 */ 55064565Sgshapiro#endif /* STARTTLS */ 55164565Sgshapiro 55238032Speter ee->e_puthdr = putheader; 55338032Speter ee->e_putbody = errbody; 55438032Speter ee->e_flags |= EF_RESPONSE|EF_METOO; 55538032Speter if (!bitset(EF_OLDSTYLE, e->e_flags)) 55638032Speter ee->e_flags &= ~EF_OLDSTYLE; 55764565Sgshapiro if (bitset(EF_DONT_MIME, e->e_flags)) 55864565Sgshapiro { 55964565Sgshapiro ee->e_flags |= EF_DONT_MIME; 56064565Sgshapiro 56164565Sgshapiro /* 56264565Sgshapiro ** If we can't convert to MIME and we don't pass 56364565Sgshapiro ** 8-bit, we can't send the body. 56464565Sgshapiro */ 56564565Sgshapiro 56664565Sgshapiro if (bitset(EF_HAS8BIT, e->e_flags) && 56764565Sgshapiro !bitset(MM_PASS8BIT, MimeMode)) 56864565Sgshapiro flags &= ~RTSF_SEND_BODY; 56964565Sgshapiro } 57064565Sgshapiro 57138032Speter ee->e_sendqueue = returnq; 57238032Speter ee->e_msgsize = ERRORFUDGE; 57364565Sgshapiro if (bitset(RTSF_SEND_BODY, flags) && 57464565Sgshapiro !bitset(PRIV_NOBODYRETN, PrivacyFlags)) 57538032Speter ee->e_msgsize += e->e_msgsize; 57638032Speter else 57738032Speter ee->e_flags |= EF_NO_BODY_RETN; 57838032Speter initsys(ee); 57964565Sgshapiro 58064565Sgshapiro#if NAMED_BIND 58164565Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 58264565Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 58364565Sgshapiro#endif /* NAMED_BIND */ 58438032Speter for (q = returnq; q != NULL; q = q->q_next) 58538032Speter { 58664565Sgshapiro if (QS_IS_BADADDR(q->q_state)) 58738032Speter continue; 58838032Speter 58938032Speter q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 59038032Speter q->q_flags |= QPINGONFAILURE; 59138032Speter 59264565Sgshapiro if (!QS_IS_DEAD(q->q_state)) 59338032Speter ee->e_nrcpts++; 59438032Speter 59538032Speter if (q->q_alias == NULL) 59664565Sgshapiro addheader("To", q->q_paddr, 0, &ee->e_header); 59738032Speter } 59838032Speter 59938032Speter if (LogLevel > 5) 60038032Speter { 60164565Sgshapiro if (bitset(EF_RESPONSE, e->e_flags)) 60238032Speter p = "return to sender"; 60364565Sgshapiro else if (bitset(EF_WARNING, e->e_flags)) 60464565Sgshapiro p = "sender notify"; 60538032Speter else if (bitset(RTSF_PM_BOUNCE, flags)) 60638032Speter p = "postmaster notify"; 60738032Speter else 60838032Speter p = "DSN"; 60938032Speter sm_syslog(LOG_INFO, e->e_id, 61064565Sgshapiro "%s: %s: %s", 61164565Sgshapiro ee->e_id, p, shortenstring(msg, MAXSHORTSTR)); 61238032Speter } 61338032Speter 61438032Speter if (SendMIMEErrors) 61538032Speter { 61664565Sgshapiro addheader("MIME-Version", "1.0", 0, &ee->e_header); 61738032Speter 61838032Speter (void) snprintf(buf, sizeof buf, "%s.%ld/%.100s", 61964565Sgshapiro ee->e_id, (long) curtime(), MyHostName); 62038032Speter ee->e_msgboundary = newstr(buf); 62138032Speter (void) snprintf(buf, sizeof buf, 62238032Speter#if DSN 62364565Sgshapiro "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"", 62464565Sgshapiro#else /* DSN */ 62564565Sgshapiro "multipart/mixed; boundary=\"%s\"", 62664565Sgshapiro#endif /* DSN */ 62764565Sgshapiro ee->e_msgboundary); 62864565Sgshapiro addheader("Content-Type", buf, 0, &ee->e_header); 62938032Speter 63038032Speter p = hvalue("Content-Transfer-Encoding", e->e_header); 63138032Speter if (p != NULL && strcasecmp(p, "binary") != 0) 63238032Speter p = NULL; 63338032Speter if (p == NULL && bitset(EF_HAS8BIT, e->e_flags)) 63438032Speter p = "8bit"; 63538032Speter if (p != NULL) 63664565Sgshapiro addheader("Content-Transfer-Encoding", 63764565Sgshapiro p, 0, &ee->e_header); 63838032Speter } 63938032Speter if (strncmp(msg, "Warning:", 8) == 0) 64038032Speter { 64164565Sgshapiro addheader("Subject", msg, 0, &ee->e_header); 64238032Speter p = "warning-timeout"; 64338032Speter } 64438032Speter else if (strncmp(msg, "Postmaster warning:", 19) == 0) 64538032Speter { 64664565Sgshapiro addheader("Subject", msg, 0, &ee->e_header); 64738032Speter p = "postmaster-warning"; 64838032Speter } 64938032Speter else if (strcmp(msg, "Return receipt") == 0) 65038032Speter { 65164565Sgshapiro addheader("Subject", msg, 0, &ee->e_header); 65238032Speter p = "return-receipt"; 65338032Speter } 65438032Speter else if (bitset(RTSF_PM_BOUNCE, flags)) 65538032Speter { 65664565Sgshapiro snprintf(buf, sizeof buf, 65764565Sgshapiro "Postmaster notify: see transcript for details"); 65864565Sgshapiro addheader("Subject", buf, 0, &ee->e_header); 65938032Speter p = "postmaster-notification"; 66038032Speter } 66138032Speter else 66238032Speter { 66364565Sgshapiro snprintf(buf, sizeof buf, 66464565Sgshapiro "Returned mail: see transcript for details"); 66564565Sgshapiro addheader("Subject", buf, 0, &ee->e_header); 66638032Speter p = "failure"; 66738032Speter } 66838032Speter (void) snprintf(buf, sizeof buf, "auto-generated (%s)", p); 66964565Sgshapiro addheader("Auto-Submitted", buf, 0, &ee->e_header); 67038032Speter 67138032Speter /* fake up an address header for the from person */ 67238032Speter expand("\201n", buf, sizeof buf, e); 67364565Sgshapiro if (parseaddr(buf, &ee->e_from, 67464565Sgshapiro RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL) 67538032Speter { 67664565Sgshapiro syserr("553 5.3.5 Can't parse myself!"); 67738032Speter ExitStat = EX_SOFTWARE; 67838032Speter returndepth--; 67964565Sgshapiro return -1; 68038032Speter } 68138032Speter ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 68238032Speter ee->e_from.q_flags |= QPINGONFAILURE; 68338032Speter ee->e_sender = ee->e_from.q_paddr; 68438032Speter 68538032Speter /* push state into submessage */ 68638032Speter CurEnv = ee; 68738032Speter define('f', "\201n", ee); 68838032Speter define('x', "Mail Delivery Subsystem", ee); 68938032Speter eatheader(ee, TRUE); 69038032Speter 69138032Speter /* mark statistics */ 69238032Speter markstats(ee, NULLADDR, FALSE); 69338032Speter 69438032Speter /* actually deliver the error message */ 69538032Speter sendall(ee, SM_DELIVER); 69638032Speter 69738032Speter /* restore state */ 69838032Speter dropenvelope(ee, TRUE); 69938032Speter CurEnv = oldcur; 70038032Speter returndepth--; 70138032Speter 70238032Speter /* check for delivery errors */ 70364565Sgshapiro if (ee->e_parent == NULL || 70464565Sgshapiro !bitset(EF_RESPONSE, ee->e_parent->e_flags)) 70538032Speter return 0; 70638032Speter for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 70738032Speter { 70864565Sgshapiro if (QS_IS_ATTEMPTED(q->q_state)) 70938032Speter return 0; 71038032Speter } 71138032Speter return -1; 71238032Speter} 71338032Speter/* 71438032Speter** ERRBODY -- output the body of an error message. 71538032Speter** 71638032Speter** Typically this is a copy of the transcript plus a copy of the 71738032Speter** original offending message. 71838032Speter** 71938032Speter** Parameters: 72038032Speter** mci -- the mailer connection information. 72138032Speter** e -- the envelope we are working in. 72238032Speter** separator -- any possible MIME separator. 72338032Speter** 72438032Speter** Returns: 72538032Speter** none 72638032Speter** 72738032Speter** Side Effects: 72838032Speter** Outputs the body of an error message. 72938032Speter*/ 73038032Speter 73164565Sgshapiro/* ARGSUSED2 */ 73264565Sgshapirostatic void 73338032Spetererrbody(mci, e, separator) 73438032Speter register MCI *mci; 73538032Speter register ENVELOPE *e; 73638032Speter char *separator; 73738032Speter{ 73864565Sgshapiro bool printheader; 73964565Sgshapiro bool sendbody; 74064565Sgshapiro bool pm_notify; 74164565Sgshapiro int save_errno; 74238032Speter register FILE *xfile; 74338032Speter char *p; 74438032Speter register ADDRESS *q = NULL; 74538032Speter char buf[MAXLINE]; 74638032Speter 74738032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 74838032Speter { 74938032Speter putline("", mci); 75038032Speter mci->mci_flags &= ~MCIF_INHEADER; 75138032Speter } 75238032Speter if (e->e_parent == NULL) 75338032Speter { 75438032Speter syserr("errbody: null parent"); 75538032Speter putline(" ----- Original message lost -----\n", mci); 75638032Speter return; 75738032Speter } 75838032Speter 75938032Speter /* 76038032Speter ** Output MIME header. 76138032Speter */ 76238032Speter 76338032Speter if (e->e_msgboundary != NULL) 76438032Speter { 76538032Speter putline("This is a MIME-encapsulated message", mci); 76638032Speter putline("", mci); 76738032Speter (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); 76838032Speter putline(buf, mci); 76938032Speter putline("", mci); 77038032Speter } 77138032Speter 77238032Speter /* 77338032Speter ** Output introductory information. 77438032Speter */ 77538032Speter 77638032Speter pm_notify = FALSE; 77738032Speter p = hvalue("subject", e->e_header); 77838032Speter if (p != NULL && strncmp(p, "Postmaster ", 11) == 0) 77938032Speter pm_notify = TRUE; 78038032Speter else 78138032Speter { 78238032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 78364565Sgshapiro { 78464565Sgshapiro if (QS_IS_BADADDR(q->q_state)) 78538032Speter break; 78664565Sgshapiro } 78738032Speter } 78838032Speter if (!pm_notify && q == NULL && 78938032Speter !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags)) 79038032Speter { 79138032Speter putline(" **********************************************", 79238032Speter mci); 79338032Speter putline(" ** THIS IS A WARNING MESSAGE ONLY **", 79438032Speter mci); 79538032Speter putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **", 79638032Speter mci); 79738032Speter putline(" **********************************************", 79838032Speter mci); 79938032Speter putline("", mci); 80038032Speter } 80138032Speter snprintf(buf, sizeof buf, "The original message was received at %s", 80264565Sgshapiro arpadate(ctime(&e->e_parent->e_ctime))); 80338032Speter putline(buf, mci); 80438032Speter expand("from \201_", buf, sizeof buf, e->e_parent); 80538032Speter putline(buf, mci); 80664565Sgshapiro 80764565Sgshapiro /* include id in postmaster copies */ 80864565Sgshapiro if (pm_notify && e->e_parent->e_id != NULL) 80964565Sgshapiro { 81064565Sgshapiro snprintf(buf, sizeof buf, "with id %s", e->e_parent->e_id); 81164565Sgshapiro putline(buf, mci); 81264565Sgshapiro } 81338032Speter putline("", mci); 81438032Speter 81538032Speter /* 81638032Speter ** Output error message header (if specified and available). 81738032Speter */ 81838032Speter 81964565Sgshapiro if (ErrMsgFile != NULL && 82064565Sgshapiro !bitset(EF_SENDRECEIPT, e->e_parent->e_flags)) 82138032Speter { 82238032Speter if (*ErrMsgFile == '/') 82338032Speter { 82464565Sgshapiro long sff = SFF_ROOTOK|SFF_REGONLY; 82538032Speter 82638032Speter if (DontLockReadFiles) 82738032Speter sff |= SFF_NOLOCK; 82864565Sgshapiro if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH, 82964565Sgshapiro DontBlameSendmail)) 83038032Speter sff |= SFF_SAFEDIRPATH; 83138032Speter xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff); 83238032Speter if (xfile != NULL) 83338032Speter { 83438032Speter while (fgets(buf, sizeof buf, xfile) != NULL) 83538032Speter { 83638032Speter translate_dollars(buf); 83738032Speter expand(buf, buf, sizeof buf, e); 83838032Speter putline(buf, mci); 83938032Speter } 84038032Speter (void) fclose(xfile); 84138032Speter putline("\n", mci); 84238032Speter } 84338032Speter } 84438032Speter else 84538032Speter { 84638032Speter expand(ErrMsgFile, buf, sizeof buf, e); 84738032Speter putline(buf, mci); 84838032Speter putline("", mci); 84938032Speter } 85038032Speter } 85138032Speter 85238032Speter /* 85338032Speter ** Output message introduction 85438032Speter */ 85538032Speter 85638032Speter printheader = TRUE; 85738032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 85838032Speter { 85964565Sgshapiro if (!QS_IS_BADADDR(q->q_state) || 86038032Speter !bitset(QPINGONFAILURE, q->q_flags)) 86138032Speter continue; 86238032Speter 86338032Speter if (printheader) 86438032Speter { 86538032Speter putline(" ----- The following addresses had permanent fatal errors -----", 86638032Speter mci); 86738032Speter printheader = FALSE; 86838032Speter } 86938032Speter 87038032Speter snprintf(buf, sizeof buf, "%s", 87138032Speter shortenstring(q->q_paddr, MAXSHORTSTR)); 87238032Speter putline(buf, mci); 87364565Sgshapiro if (q->q_rstatus != NULL) 87464565Sgshapiro { 87564565Sgshapiro snprintf(buf, sizeof buf, " (reason: %s)", 87664565Sgshapiro shortenstring(exitstat(q->q_rstatus), 87764565Sgshapiro MAXSHORTSTR)); 87864565Sgshapiro putline(buf, mci); 87964565Sgshapiro } 88038032Speter if (q->q_alias != NULL) 88138032Speter { 88238032Speter snprintf(buf, sizeof buf, " (expanded from: %s)", 88364565Sgshapiro shortenstring(q->q_alias->q_paddr, 88464565Sgshapiro MAXSHORTSTR)); 88538032Speter putline(buf, mci); 88638032Speter } 88738032Speter } 88838032Speter if (!printheader) 88938032Speter putline("", mci); 89038032Speter 89138032Speter printheader = TRUE; 89238032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 89338032Speter { 89464565Sgshapiro if (QS_IS_BADADDR(q->q_state) || 89538032Speter !bitset(QPRIMARY, q->q_flags) || 89638032Speter !bitset(QDELAYED, q->q_flags)) 89738032Speter continue; 89838032Speter 89938032Speter if (printheader) 90038032Speter { 90138032Speter putline(" ----- The following addresses had transient non-fatal errors -----", 90238032Speter mci); 90338032Speter printheader = FALSE; 90438032Speter } 90538032Speter 90638032Speter snprintf(buf, sizeof buf, "%s", 90738032Speter shortenstring(q->q_paddr, MAXSHORTSTR)); 90838032Speter putline(buf, mci); 90938032Speter if (q->q_alias != NULL) 91038032Speter { 91138032Speter snprintf(buf, sizeof buf, " (expanded from: %s)", 91264565Sgshapiro shortenstring(q->q_alias->q_paddr, 91364565Sgshapiro MAXSHORTSTR)); 91438032Speter putline(buf, mci); 91538032Speter } 91638032Speter } 91738032Speter if (!printheader) 91838032Speter putline("", mci); 91938032Speter 92038032Speter printheader = TRUE; 92138032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 92238032Speter { 92364565Sgshapiro if (QS_IS_BADADDR(q->q_state) || 92438032Speter !bitset(QPRIMARY, q->q_flags) || 92538032Speter bitset(QDELAYED, q->q_flags)) 92638032Speter continue; 92738032Speter else if (!bitset(QPINGONSUCCESS, q->q_flags)) 92838032Speter continue; 92938032Speter else if (bitset(QRELAYED, q->q_flags)) 93038032Speter p = "relayed to non-DSN-aware mailer"; 93138032Speter else if (bitset(QDELIVERED, q->q_flags)) 93238032Speter { 93338032Speter if (bitset(QEXPANDED, q->q_flags)) 93438032Speter p = "successfully delivered to mailing list"; 93538032Speter else 93638032Speter p = "successfully delivered to mailbox"; 93738032Speter } 93838032Speter else if (bitset(QEXPANDED, q->q_flags)) 93938032Speter p = "expanded by alias"; 94038032Speter else 94138032Speter continue; 94238032Speter 94338032Speter if (printheader) 94438032Speter { 94538032Speter putline(" ----- The following addresses had successful delivery notifications -----", 94638032Speter mci); 94738032Speter printheader = FALSE; 94838032Speter } 94938032Speter 95038032Speter snprintf(buf, sizeof buf, "%s (%s)", 95164565Sgshapiro shortenstring(q->q_paddr, MAXSHORTSTR), p); 95238032Speter putline(buf, mci); 95338032Speter if (q->q_alias != NULL) 95438032Speter { 95538032Speter snprintf(buf, sizeof buf, " (expanded from: %s)", 95664565Sgshapiro shortenstring(q->q_alias->q_paddr, 95764565Sgshapiro MAXSHORTSTR)); 95838032Speter putline(buf, mci); 95938032Speter } 96038032Speter } 96138032Speter if (!printheader) 96238032Speter putline("", mci); 96338032Speter 96438032Speter /* 96538032Speter ** Output transcript of errors 96638032Speter */ 96738032Speter 96838032Speter (void) fflush(stdout); 96964565Sgshapiro if (e->e_parent->e_xfp == NULL) 97038032Speter { 97164565Sgshapiro putline(" ----- Transcript of session is unavailable -----\n", 97264565Sgshapiro mci); 97338032Speter } 97438032Speter else 97538032Speter { 97638032Speter printheader = TRUE; 97764565Sgshapiro (void) bfrewind(e->e_parent->e_xfp); 97838032Speter if (e->e_xfp != NULL) 97938032Speter (void) fflush(e->e_xfp); 98064565Sgshapiro while (fgets(buf, sizeof buf, e->e_parent->e_xfp) != NULL) 98138032Speter { 98238032Speter if (printheader) 98364565Sgshapiro putline(" ----- Transcript of session follows -----\n", 98464565Sgshapiro mci); 98538032Speter printheader = FALSE; 98638032Speter putline(buf, mci); 98738032Speter } 98838032Speter } 98938032Speter errno = 0; 99038032Speter 99138032Speter#if DSN 99238032Speter /* 99338032Speter ** Output machine-readable version. 99438032Speter */ 99538032Speter 99638032Speter if (e->e_msgboundary != NULL) 99738032Speter { 99838032Speter putline("", mci); 99938032Speter (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); 100038032Speter putline(buf, mci); 100138032Speter putline("Content-Type: message/delivery-status", mci); 100238032Speter putline("", mci); 100338032Speter 100438032Speter /* 100538032Speter ** Output per-message information. 100638032Speter */ 100738032Speter 100838032Speter /* original envelope id from MAIL FROM: line */ 100938032Speter if (e->e_parent->e_envid != NULL) 101038032Speter { 101164565Sgshapiro (void) snprintf(buf, sizeof buf, 101264565Sgshapiro "Original-Envelope-Id: %.800s", 101364565Sgshapiro xuntextify(e->e_parent->e_envid)); 101438032Speter putline(buf, mci); 101538032Speter } 101638032Speter 101738032Speter /* Reporting-MTA: is us (required) */ 101838032Speter (void) snprintf(buf, sizeof buf, "Reporting-MTA: dns; %.800s", MyHostName); 101938032Speter putline(buf, mci); 102038032Speter 102138032Speter /* DSN-Gateway: not relevant since we are not translating */ 102238032Speter 102338032Speter /* Received-From-MTA: shows where we got this message from */ 102438032Speter if (RealHostName != NULL) 102538032Speter { 102638032Speter /* XXX use $s for type? */ 102738032Speter if (e->e_parent->e_from.q_mailer == NULL || 102838032Speter (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL) 102938032Speter p = "dns"; 103064565Sgshapiro (void) snprintf(buf, sizeof buf, 103164565Sgshapiro "Received-From-MTA: %s; %.800s", 103264565Sgshapiro p, RealHostName); 103338032Speter putline(buf, mci); 103438032Speter } 103538032Speter 103638032Speter /* Arrival-Date: -- when it arrived here */ 103738032Speter (void) snprintf(buf, sizeof buf, "Arrival-Date: %s", 103864565Sgshapiro arpadate(ctime(&e->e_parent->e_ctime))); 103938032Speter putline(buf, mci); 104038032Speter 104138032Speter /* 104238032Speter ** Output per-address information. 104338032Speter */ 104438032Speter 104538032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 104638032Speter { 104738032Speter register ADDRESS *r; 104838032Speter char *action; 104938032Speter 105064565Sgshapiro if (QS_IS_BADADDR(q->q_state)) 105138032Speter action = "failed"; 105238032Speter else if (!bitset(QPRIMARY, q->q_flags)) 105338032Speter continue; 105438032Speter else if (bitset(QDELIVERED, q->q_flags)) 105538032Speter { 105638032Speter if (bitset(QEXPANDED, q->q_flags)) 105738032Speter action = "delivered (to mailing list)"; 105838032Speter else 105938032Speter action = "delivered (to mailbox)"; 106038032Speter } 106138032Speter else if (bitset(QRELAYED, q->q_flags)) 106238032Speter action = "relayed (to non-DSN-aware mailer)"; 106338032Speter else if (bitset(QEXPANDED, q->q_flags)) 106438032Speter action = "expanded (to multi-recipient alias)"; 106538032Speter else if (bitset(QDELAYED, q->q_flags)) 106638032Speter action = "delayed"; 106738032Speter else 106838032Speter continue; 106938032Speter 107038032Speter putline("", mci); 107138032Speter 107238032Speter /* Original-Recipient: -- passed from on high */ 107338032Speter if (q->q_orcpt != NULL) 107438032Speter { 107564565Sgshapiro (void) snprintf(buf, sizeof buf, 107664565Sgshapiro "Original-Recipient: %.800s", 107764565Sgshapiro q->q_orcpt); 107838032Speter putline(buf, mci); 107938032Speter } 108038032Speter 108138032Speter /* Final-Recipient: -- the name from the RCPT command */ 108238032Speter p = e->e_parent->e_from.q_mailer->m_addrtype; 108338032Speter if (p == NULL) 108438032Speter p = "rfc822"; 108538032Speter for (r = q; r->q_alias != NULL; r = r->q_alias) 108638032Speter continue; 108764565Sgshapiro if (strcasecmp(p, "rfc822") != 0) 108838032Speter { 108938032Speter (void) snprintf(buf, sizeof buf, 109064565Sgshapiro "Final-Recipient: %s; %.800s", 109164565Sgshapiro r->q_mailer->m_addrtype, 109264565Sgshapiro r->q_user); 109338032Speter } 109464565Sgshapiro else if (strchr(r->q_user, '@') != NULL) 109564565Sgshapiro { 109664565Sgshapiro (void) snprintf(buf, sizeof buf, 109764565Sgshapiro "Final-Recipient: %s; %.800s", 109864565Sgshapiro p, r->q_user); 109964565Sgshapiro } 110038032Speter else if (strchr(r->q_paddr, '@') != NULL) 110138032Speter { 110264565Sgshapiro char *qp; 110364565Sgshapiro bool b; 110464565Sgshapiro 110564565Sgshapiro qp = r->q_paddr; 110664565Sgshapiro /* strip brackets from address */ 110764565Sgshapiro b = FALSE; 110864565Sgshapiro if (*qp == '<') 110964565Sgshapiro { 111064565Sgshapiro b = qp[strlen(qp) - 1] == '>'; 111164565Sgshapiro if (b) 111264565Sgshapiro qp[strlen(qp) - 1] = '\0'; 111364565Sgshapiro qp++; 111464565Sgshapiro } 111538032Speter (void) snprintf(buf, sizeof buf, 111664565Sgshapiro "Final-Recipient: %s; %.800s", 111764565Sgshapiro p, qp); 111864565Sgshapiro /* undo damage */ 111964565Sgshapiro if (b) 112064565Sgshapiro qp[strlen(qp)] = '>'; 112138032Speter } 112238032Speter else 112338032Speter { 112438032Speter (void) snprintf(buf, sizeof buf, 112564565Sgshapiro "Final-Recipient: %s; %.700s@%.100s", 112664565Sgshapiro p, r->q_user, MyHostName); 112738032Speter } 112838032Speter putline(buf, mci); 112938032Speter 113038032Speter /* X-Actual-Recipient: -- the real problem address */ 113138032Speter if (r != q && q->q_user[0] != '\0') 113238032Speter { 113364565Sgshapiro if (q->q_mailer != NULL && 113464565Sgshapiro q->q_mailer->m_addrtype != NULL) 113564565Sgshapiro p = q->q_mailer->m_addrtype; 113664565Sgshapiro else 113764565Sgshapiro p = "rfc822"; 113864565Sgshapiro 113964565Sgshapiro if (strcasecmp(p, "rfc822") == 0 && 114064565Sgshapiro strchr(q->q_user, '@') == NULL) 114138032Speter { 114238032Speter (void) snprintf(buf, sizeof buf, 114364565Sgshapiro "X-Actual-Recipient: %s; %.700s@%.100s", 114464565Sgshapiro p, q->q_user, 114564565Sgshapiro MyHostName); 114638032Speter } 114738032Speter else 114838032Speter { 114938032Speter (void) snprintf(buf, sizeof buf, 115064565Sgshapiro "X-Actual-Recipient: %s; %.800s", 115164565Sgshapiro p, q->q_user); 115238032Speter } 115338032Speter putline(buf, mci); 115438032Speter } 115538032Speter 115638032Speter /* Action: -- what happened? */ 115738032Speter snprintf(buf, sizeof buf, "Action: %s", action); 115838032Speter putline(buf, mci); 115938032Speter 116038032Speter /* Status: -- what _really_ happened? */ 116138032Speter if (q->q_status != NULL) 116238032Speter p = q->q_status; 116364565Sgshapiro else if (QS_IS_BADADDR(q->q_state)) 116438032Speter p = "5.0.0"; 116564565Sgshapiro else if (QS_IS_QUEUEUP(q->q_state)) 116638032Speter p = "4.0.0"; 116738032Speter else 116838032Speter p = "2.0.0"; 116938032Speter snprintf(buf, sizeof buf, "Status: %s", p); 117038032Speter putline(buf, mci); 117138032Speter 117238032Speter /* Remote-MTA: -- who was I talking to? */ 117338032Speter if (q->q_statmta != NULL) 117438032Speter { 117538032Speter if (q->q_mailer == NULL || 117638032Speter (p = q->q_mailer->m_mtatype) == NULL) 117738032Speter p = "dns"; 117838032Speter (void) snprintf(buf, sizeof buf, 117964565Sgshapiro "Remote-MTA: %s; %.800s", 118064565Sgshapiro p, q->q_statmta); 118138032Speter p = &buf[strlen(buf) - 1]; 118238032Speter if (*p == '.') 118338032Speter *p = '\0'; 118438032Speter putline(buf, mci); 118538032Speter } 118638032Speter 118738032Speter /* Diagnostic-Code: -- actual result from other end */ 118838032Speter if (q->q_rstatus != NULL) 118938032Speter { 119038032Speter p = q->q_mailer->m_diagtype; 119138032Speter if (p == NULL) 119238032Speter p = "smtp"; 119338032Speter (void) snprintf(buf, sizeof buf, 119464565Sgshapiro "Diagnostic-Code: %s; %.800s", 119564565Sgshapiro p, q->q_rstatus); 119638032Speter putline(buf, mci); 119738032Speter } 119838032Speter 119938032Speter /* Last-Attempt-Date: -- fine granularity */ 120038032Speter if (q->q_statdate == (time_t) 0L) 120138032Speter q->q_statdate = curtime(); 120238032Speter (void) snprintf(buf, sizeof buf, 120364565Sgshapiro "Last-Attempt-Date: %s", 120464565Sgshapiro arpadate(ctime(&q->q_statdate))); 120538032Speter putline(buf, mci); 120638032Speter 120738032Speter /* Will-Retry-Until: -- for delayed messages only */ 120864565Sgshapiro if (QS_IS_QUEUEUP(q->q_state)) 120938032Speter { 121038032Speter time_t xdate; 121138032Speter 121238032Speter xdate = e->e_parent->e_ctime + 121338032Speter TimeOuts.to_q_return[e->e_parent->e_timeoutclass]; 121438032Speter snprintf(buf, sizeof buf, 121564565Sgshapiro "Will-Retry-Until: %s", 121664565Sgshapiro arpadate(ctime(&xdate))); 121738032Speter putline(buf, mci); 121838032Speter } 121938032Speter } 122038032Speter } 122164565Sgshapiro#endif /* DSN */ 122238032Speter 122338032Speter /* 122438032Speter ** Output text of original message 122538032Speter */ 122638032Speter 122738032Speter putline("", mci); 122838032Speter if (bitset(EF_HAS_DF, e->e_parent->e_flags)) 122938032Speter { 123038032Speter sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) && 123138032Speter !bitset(EF_NO_BODY_RETN, e->e_flags); 123238032Speter 123338032Speter if (e->e_msgboundary == NULL) 123438032Speter { 123538032Speter if (sendbody) 123638032Speter putline(" ----- Original message follows -----\n", mci); 123738032Speter else 123838032Speter putline(" ----- Message header follows -----\n", mci); 123938032Speter } 124038032Speter else 124138032Speter { 124238032Speter (void) snprintf(buf, sizeof buf, "--%s", 124364565Sgshapiro e->e_msgboundary); 124438032Speter 124538032Speter putline(buf, mci); 124638032Speter (void) snprintf(buf, sizeof buf, "Content-Type: %s", 124764565Sgshapiro sendbody ? "message/rfc822" 124864565Sgshapiro : "text/rfc822-headers"); 124938032Speter putline(buf, mci); 125038032Speter 125164565Sgshapiro p = hvalue("Content-Transfer-Encoding", 125264565Sgshapiro e->e_parent->e_header); 125338032Speter if (p != NULL && strcasecmp(p, "binary") != 0) 125438032Speter p = NULL; 125564565Sgshapiro if (p == NULL && 125664565Sgshapiro bitset(EF_HAS8BIT, e->e_parent->e_flags)) 125738032Speter p = "8bit"; 125838032Speter if (p != NULL) 125938032Speter { 126064565Sgshapiro (void) snprintf(buf, sizeof buf, 126164565Sgshapiro "Content-Transfer-Encoding: %s", 126264565Sgshapiro p); 126338032Speter putline(buf, mci); 126438032Speter } 126538032Speter } 126638032Speter putline("", mci); 126764565Sgshapiro save_errno = errno; 126843733Speter putheader(mci, e->e_parent->e_header, e->e_parent, M87F_OUTER); 126964565Sgshapiro errno = save_errno; 127038032Speter if (sendbody) 127138032Speter putbody(mci, e->e_parent, e->e_msgboundary); 127238032Speter else if (e->e_msgboundary == NULL) 127338032Speter { 127438032Speter putline("", mci); 127538032Speter putline(" ----- Message body suppressed -----", mci); 127638032Speter } 127738032Speter } 127838032Speter else if (e->e_msgboundary == NULL) 127938032Speter { 128038032Speter putline(" ----- No message was collected -----\n", mci); 128138032Speter } 128238032Speter 128338032Speter if (e->e_msgboundary != NULL) 128438032Speter { 128538032Speter putline("", mci); 128638032Speter (void) snprintf(buf, sizeof buf, "--%s--", e->e_msgboundary); 128738032Speter putline(buf, mci); 128838032Speter } 128938032Speter putline("", mci); 129064565Sgshapiro (void) fflush(mci->mci_out); 129138032Speter 129238032Speter /* 129338032Speter ** Cleanup and exit 129438032Speter */ 129538032Speter 129638032Speter if (errno != 0) 129738032Speter syserr("errbody: I/O error"); 129838032Speter} 129938032Speter/* 130038032Speter** SMTPTODSN -- convert SMTP to DSN status code 130138032Speter** 130238032Speter** Parameters: 130338032Speter** smtpstat -- the smtp status code (e.g., 550). 130438032Speter** 130538032Speter** Returns: 130638032Speter** The DSN version of the status code. 130738032Speter*/ 130838032Speter 130938032Speterchar * 131038032Spetersmtptodsn(smtpstat) 131138032Speter int smtpstat; 131238032Speter{ 131338032Speter if (smtpstat < 0) 131438032Speter return "4.4.2"; 131538032Speter 131638032Speter switch (smtpstat) 131738032Speter { 131838032Speter case 450: /* Req mail action not taken: mailbox unavailable */ 131938032Speter return "4.2.0"; 132038032Speter 132138032Speter case 451: /* Req action aborted: local error in processing */ 132238032Speter return "4.3.0"; 132338032Speter 132438032Speter case 452: /* Req action not taken: insufficient sys storage */ 132538032Speter return "4.3.1"; 132638032Speter 132738032Speter case 500: /* Syntax error, command unrecognized */ 132838032Speter return "5.5.2"; 132938032Speter 133038032Speter case 501: /* Syntax error in parameters or arguments */ 133138032Speter return "5.5.4"; 133238032Speter 133338032Speter case 502: /* Command not implemented */ 133438032Speter return "5.5.1"; 133538032Speter 133638032Speter case 503: /* Bad sequence of commands */ 133738032Speter return "5.5.1"; 133838032Speter 133938032Speter case 504: /* Command parameter not implemented */ 134038032Speter return "5.5.4"; 134138032Speter 134238032Speter case 550: /* Req mail action not taken: mailbox unavailable */ 134338032Speter return "5.2.0"; 134438032Speter 134538032Speter case 551: /* User not local; please try <...> */ 134638032Speter return "5.1.6"; 134738032Speter 134838032Speter case 552: /* Req mail action aborted: exceeded storage alloc */ 134938032Speter return "5.2.2"; 135038032Speter 135138032Speter case 553: /* Req action not taken: mailbox name not allowed */ 135238032Speter return "5.1.0"; 135338032Speter 135438032Speter case 554: /* Transaction failed */ 135538032Speter return "5.0.0"; 135638032Speter } 135738032Speter 135838032Speter if ((smtpstat / 100) == 2) 135938032Speter return "2.0.0"; 136038032Speter if ((smtpstat / 100) == 4) 136138032Speter return "4.0.0"; 136238032Speter return "5.0.0"; 136338032Speter} 136438032Speter/* 136538032Speter** XTEXTIFY -- take regular text and turn it into DSN-style xtext 136638032Speter** 136738032Speter** Parameters: 136838032Speter** t -- the text to convert. 136938032Speter** taboo -- additional characters that must be encoded. 137038032Speter** 137138032Speter** Returns: 137238032Speter** The xtext-ified version of the same string. 137338032Speter*/ 137438032Speter 137538032Speterchar * 137638032Speterxtextify(t, taboo) 137738032Speter register char *t; 137838032Speter char *taboo; 137938032Speter{ 138038032Speter register char *p; 138138032Speter int l; 138238032Speter int nbogus; 138338032Speter static char *bp = NULL; 138438032Speter static int bplen = 0; 138538032Speter 138638032Speter if (taboo == NULL) 138738032Speter taboo = ""; 138838032Speter 138938032Speter /* figure out how long this xtext will have to be */ 139038032Speter nbogus = l = 0; 139138032Speter for (p = t; *p != '\0'; p++) 139238032Speter { 139338032Speter register int c = (*p & 0xff); 139438032Speter 139538032Speter /* ASCII dependence here -- this is the way the spec words it */ 139638032Speter if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || 139738032Speter strchr(taboo, c) != NULL) 139838032Speter nbogus++; 139938032Speter l++; 140038032Speter } 140138032Speter if (nbogus == 0) 140238032Speter return t; 140338032Speter l += nbogus * 2 + 1; 140438032Speter 140538032Speter /* now allocate space if necessary for the new string */ 140638032Speter if (l > bplen) 140738032Speter { 140838032Speter if (bp != NULL) 140938032Speter free(bp); 141038032Speter bp = xalloc(l); 141138032Speter bplen = l; 141238032Speter } 141338032Speter 141438032Speter /* ok, copy the text with byte expansion */ 141538032Speter for (p = bp; *t != '\0'; ) 141638032Speter { 141738032Speter register int c = (*t++ & 0xff); 141838032Speter 141938032Speter /* ASCII dependence here -- this is the way the spec words it */ 142038032Speter if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || 142138032Speter strchr(taboo, c) != NULL) 142238032Speter { 142338032Speter *p++ = '+'; 142464565Sgshapiro *p++ = "0123456789ABCDEF"[c >> 4]; 142564565Sgshapiro *p++ = "0123456789ABCDEF"[c & 0xf]; 142638032Speter } 142738032Speter else 142838032Speter *p++ = c; 142938032Speter } 143038032Speter *p = '\0'; 143138032Speter return bp; 143238032Speter} 143338032Speter/* 143438032Speter** XUNTEXTIFY -- take xtext and turn it into plain text 143538032Speter** 143638032Speter** Parameters: 143738032Speter** t -- the xtextified text. 143838032Speter** 143938032Speter** Returns: 144038032Speter** The decoded text. No attempt is made to deal with 144138032Speter** null strings in the resulting text. 144238032Speter*/ 144338032Speter 144438032Speterchar * 144538032Speterxuntextify(t) 144638032Speter register char *t; 144738032Speter{ 144838032Speter register char *p; 144938032Speter int l; 145038032Speter static char *bp = NULL; 145138032Speter static int bplen = 0; 145238032Speter 145338032Speter /* heuristic -- if no plus sign, just return the input */ 145438032Speter if (strchr(t, '+') == NULL) 145538032Speter return t; 145638032Speter 145738032Speter /* xtext is always longer than decoded text */ 145838032Speter l = strlen(t); 145938032Speter if (l > bplen) 146038032Speter { 146138032Speter if (bp != NULL) 146238032Speter free(bp); 146338032Speter bp = xalloc(l); 146438032Speter bplen = l; 146538032Speter } 146638032Speter 146738032Speter /* ok, copy the text with byte compression */ 146838032Speter for (p = bp; *t != '\0'; t++) 146938032Speter { 147038032Speter register int c = *t & 0xff; 147138032Speter 147238032Speter if (c != '+') 147338032Speter { 147438032Speter *p++ = c; 147538032Speter continue; 147638032Speter } 147738032Speter 147838032Speter c = *++t & 0xff; 147938032Speter if (!isascii(c) || !isxdigit(c)) 148038032Speter { 148138032Speter /* error -- first digit is not hex */ 148238032Speter usrerr("bogus xtext: +%c", c); 148338032Speter t--; 148438032Speter continue; 148538032Speter } 148638032Speter if (isdigit(c)) 148738032Speter c -= '0'; 148838032Speter else if (isupper(c)) 148938032Speter c -= 'A' - 10; 149038032Speter else 149138032Speter c -= 'a' - 10; 149238032Speter *p = c << 4; 149338032Speter 149438032Speter c = *++t & 0xff; 149538032Speter if (!isascii(c) || !isxdigit(c)) 149638032Speter { 149738032Speter /* error -- second digit is not hex */ 149838032Speter usrerr("bogus xtext: +%x%c", *p >> 4, c); 149938032Speter t--; 150038032Speter continue; 150138032Speter } 150238032Speter if (isdigit(c)) 150338032Speter c -= '0'; 150438032Speter else if (isupper(c)) 150538032Speter c -= 'A' - 10; 150638032Speter else 150738032Speter c -= 'a' - 10; 150838032Speter *p++ |= c; 150938032Speter } 151038032Speter *p = '\0'; 151138032Speter return bp; 151238032Speter} 151338032Speter/* 151438032Speter** XTEXTOK -- check if a string is legal xtext 151538032Speter** 151638032Speter** Xtext is used in Delivery Status Notifications. The spec was 151738032Speter** taken from RFC 1891, ``SMTP Service Extension for Delivery 151838032Speter** Status Notifications''. 151938032Speter** 152038032Speter** Parameters: 152138032Speter** s -- the string to check. 152238032Speter** 152338032Speter** Returns: 152438032Speter** TRUE -- if 's' is legal xtext. 152538032Speter** FALSE -- if it has any illegal characters in it. 152638032Speter*/ 152738032Speter 152838032Speterbool 152938032Speterxtextok(s) 153038032Speter char *s; 153138032Speter{ 153238032Speter int c; 153338032Speter 153438032Speter while ((c = *s++) != '\0') 153538032Speter { 153638032Speter if (c == '+') 153738032Speter { 153838032Speter c = *s++; 153938032Speter if (!isascii(c) || !isxdigit(c)) 154038032Speter return FALSE; 154138032Speter c = *s++; 154238032Speter if (!isascii(c) || !isxdigit(c)) 154338032Speter return FALSE; 154438032Speter } 154538032Speter else if (c < '!' || c > '~' || c == '=') 154638032Speter return FALSE; 154738032Speter } 154838032Speter return TRUE; 154938032Speter} 155038032Speter/* 155138032Speter** PRUNEROUTE -- prune an RFC-822 source route 155264565Sgshapiro** 155338032Speter** Trims down a source route to the last internet-registered hop. 155438032Speter** This is encouraged by RFC 1123 section 5.3.3. 155564565Sgshapiro** 155638032Speter** Parameters: 155738032Speter** addr -- the address 155864565Sgshapiro** 155938032Speter** Returns: 156038032Speter** TRUE -- address was modified 156138032Speter** FALSE -- address could not be pruned 156264565Sgshapiro** 156338032Speter** Side Effects: 156438032Speter** modifies addr in-place 156538032Speter*/ 156638032Speter 156764565Sgshapirostatic bool 156838032Speterpruneroute(addr) 156938032Speter char *addr; 157038032Speter{ 157138032Speter#if NAMED_BIND 157238032Speter char *start, *at, *comma; 157338032Speter char c; 157438032Speter int rcode; 157538032Speter int i; 157638032Speter char hostbuf[BUFSIZ]; 157738032Speter char *mxhosts[MAXMXHOSTS + 1]; 157838032Speter 157938032Speter /* check to see if this is really a route-addr */ 158038032Speter if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') 158138032Speter return FALSE; 158238032Speter start = strchr(addr, ':'); 158338032Speter at = strrchr(addr, '@'); 158438032Speter if (start == NULL || at == NULL || at < start) 158538032Speter return FALSE; 158638032Speter 158738032Speter /* slice off the angle brackets */ 158838032Speter i = strlen(at + 1); 158938032Speter if (i >= (SIZE_T) sizeof hostbuf) 159038032Speter return FALSE; 159164565Sgshapiro (void) strlcpy(hostbuf, at + 1, sizeof hostbuf); 159238032Speter hostbuf[i - 1] = '\0'; 159338032Speter 159438032Speter while (start) 159538032Speter { 159664565Sgshapiro if (getmxrr(hostbuf, mxhosts, NULL, FALSE, &rcode) > 0) 159738032Speter { 159864565Sgshapiro (void) strlcpy(addr + 1, start + 1, strlen(addr) - 1); 159938032Speter return TRUE; 160038032Speter } 160138032Speter c = *start; 160238032Speter *start = '\0'; 160338032Speter comma = strrchr(addr, ','); 160438032Speter if (comma != NULL && comma[1] == '@' && 160538032Speter strlen(comma + 2) < (SIZE_T) sizeof hostbuf) 160664565Sgshapiro (void) strlcpy(hostbuf, comma + 2, sizeof hostbuf); 160738032Speter else 160838032Speter comma = NULL; 160938032Speter *start = c; 161038032Speter start = comma; 161138032Speter } 161264565Sgshapiro#endif /* NAMED_BIND */ 161338032Speter return FALSE; 161438032Speter} 1615