savemail.c revision 43733
138032Speter/* 238032Speter * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 338032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 438032Speter * Copyright (c) 1988, 1993 538032Speter * The Regents of the University of California. All rights reserved. 638032Speter * 738032Speter * By using this file, you agree to the terms and conditions set 838032Speter * forth in the LICENSE file which can be found at the top level of 938032Speter * the sendmail distribution. 1038032Speter * 1138032Speter */ 1238032Speter 1338032Speter#ifndef lint 1443733Speterstatic char sccsid[] = "@(#)savemail.c 8.140 (Berkeley) 1/18/1999"; 1538032Speter#endif /* not lint */ 1638032Speter 1738032Speter# include "sendmail.h" 1838032Speter 1938032Speter/* 2038032Speter** SAVEMAIL -- Save mail on error 2138032Speter** 2238032Speter** If mailing back errors, mail it back to the originator 2338032Speter** together with an error message; otherwise, just put it in 2438032Speter** dead.letter in the user's home directory (if he exists on 2538032Speter** this machine). 2638032Speter** 2738032Speter** Parameters: 2838032Speter** e -- the envelope containing the message in error. 2938032Speter** sendbody -- if TRUE, also send back the body of the 3038032Speter** message; otherwise just send the header. 3138032Speter** 3238032Speter** Returns: 3338032Speter** none 3438032Speter** 3538032Speter** Side Effects: 3638032Speter** Saves the letter, by writing or mailing it back to the 3738032Speter** sender, or by putting it in dead.letter in her home 3838032Speter** directory. 3938032Speter*/ 4038032Speter 4138032Speter/* defines for state machine */ 4238032Speter# define ESM_REPORT 0 /* report to sender's terminal */ 4338032Speter# define ESM_MAIL 1 /* mail back to sender */ 4438032Speter# define ESM_QUIET 2 /* messages have already been returned */ 4538032Speter# define ESM_DEADLETTER 3 /* save in ~/dead.letter */ 4638032Speter# define ESM_POSTMASTER 4 /* return to postmaster */ 4738032Speter# define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */ 4838032Speter# define ESM_PANIC 6 /* leave the locked queue/transcript files */ 4938032Speter# define ESM_DONE 7 /* the message is successfully delivered */ 5038032Speter 5138032Speter 5238032Spetervoid 5338032Spetersavemail(e, sendbody) 5438032Speter register ENVELOPE *e; 5538032Speter bool sendbody; 5638032Speter{ 5738032Speter register struct passwd *pw; 5838032Speter register FILE *fp; 5938032Speter int state; 6038032Speter auto ADDRESS *q = NULL; 6138032Speter register char *p; 6238032Speter MCI mcibuf; 6338032Speter int flags; 6438032Speter char buf[MAXLINE+1]; 6538032Speter extern char *ttypath __P((void)); 6638032Speter extern bool writable __P((char *, ADDRESS *, int)); 6738032Speter 6838032Speter if (tTd(6, 1)) 6938032Speter { 7038032Speter printf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=", 7138032Speter e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id, 7238032Speter ExitStat); 7338032Speter printaddr(&e->e_from, FALSE); 7438032Speter } 7538032Speter 7638032Speter if (e->e_id == NULL) 7738032Speter { 7838032Speter /* can't return a message with no id */ 7938032Speter return; 8038032Speter } 8138032Speter 8238032Speter /* 8338032Speter ** In the unhappy event we don't know who to return the mail 8438032Speter ** to, make someone up. 8538032Speter */ 8638032Speter 8738032Speter if (e->e_from.q_paddr == NULL) 8838032Speter { 8938032Speter e->e_sender = "Postmaster"; 9038032Speter if (parseaddr(e->e_sender, &e->e_from, 9138032Speter RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL) 9238032Speter { 9338032Speter syserr("553 Cannot parse Postmaster!"); 9442580Speter finis(TRUE, EX_SOFTWARE); 9538032Speter } 9638032Speter } 9738032Speter e->e_to = NULL; 9838032Speter 9938032Speter /* 10038032Speter ** Basic state machine. 10138032Speter ** 10238032Speter ** This machine runs through the following states: 10338032Speter ** 10438032Speter ** ESM_QUIET Errors have already been printed iff the 10538032Speter ** sender is local. 10638032Speter ** ESM_REPORT Report directly to the sender's terminal. 10738032Speter ** ESM_MAIL Mail response to the sender. 10838032Speter ** ESM_DEADLETTER Save response in ~/dead.letter. 10938032Speter ** ESM_POSTMASTER Mail response to the postmaster. 11038032Speter ** ESM_PANIC Save response anywhere possible. 11138032Speter */ 11238032Speter 11338032Speter /* determine starting state */ 11438032Speter switch (e->e_errormode) 11538032Speter { 11638032Speter case EM_WRITE: 11738032Speter state = ESM_REPORT; 11838032Speter break; 11938032Speter 12038032Speter case EM_BERKNET: 12138032Speter case EM_MAIL: 12238032Speter state = ESM_MAIL; 12338032Speter break; 12438032Speter 12538032Speter case EM_PRINT: 12638032Speter case '\0': 12738032Speter state = ESM_QUIET; 12838032Speter break; 12938032Speter 13038032Speter case EM_QUIET: 13138032Speter /* no need to return anything at all */ 13238032Speter return; 13338032Speter 13438032Speter default: 13538032Speter syserr("554 savemail: bogus errormode x%x\n", e->e_errormode); 13638032Speter state = ESM_MAIL; 13738032Speter break; 13838032Speter } 13938032Speter 14038032Speter /* if this is already an error response, send to postmaster */ 14138032Speter if (bitset(EF_RESPONSE, e->e_flags)) 14238032Speter { 14338032Speter if (e->e_parent != NULL && 14438032Speter bitset(EF_RESPONSE, e->e_parent->e_flags)) 14538032Speter { 14638032Speter /* got an error sending a response -- can it */ 14738032Speter return; 14838032Speter } 14938032Speter state = ESM_POSTMASTER; 15038032Speter } 15138032Speter 15238032Speter while (state != ESM_DONE) 15338032Speter { 15438032Speter if (tTd(6, 5)) 15538032Speter printf(" state %d\n", state); 15638032Speter 15738032Speter switch (state) 15838032Speter { 15938032Speter case ESM_QUIET: 16038032Speter if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags)) 16138032Speter state = ESM_DEADLETTER; 16238032Speter else 16338032Speter state = ESM_MAIL; 16438032Speter break; 16538032Speter 16638032Speter case ESM_REPORT: 16738032Speter 16838032Speter /* 16938032Speter ** If the user is still logged in on the same terminal, 17038032Speter ** then write the error messages back to hir (sic). 17138032Speter */ 17238032Speter 17338032Speter p = ttypath(); 17438032Speter if (p == NULL || freopen(p, "w", stdout) == NULL) 17538032Speter { 17638032Speter state = ESM_MAIL; 17738032Speter break; 17838032Speter } 17938032Speter 18038032Speter expand("\201n", buf, sizeof buf, e); 18138032Speter printf("\r\nMessage from %s...\r\n", buf); 18238032Speter printf("Errors occurred while sending mail.\r\n"); 18338032Speter if (e->e_xfp != NULL) 18438032Speter { 18538032Speter (void) fflush(e->e_xfp); 18638032Speter fp = fopen(queuename(e, 'x'), "r"); 18738032Speter } 18838032Speter else 18938032Speter fp = NULL; 19038032Speter if (fp == NULL) 19138032Speter { 19238032Speter syserr("Cannot open %s", queuename(e, 'x')); 19338032Speter printf("Transcript of session is unavailable.\r\n"); 19438032Speter } 19538032Speter else 19638032Speter { 19738032Speter printf("Transcript follows:\r\n"); 19838032Speter while (fgets(buf, sizeof buf, fp) != NULL && 19938032Speter !ferror(stdout)) 20038032Speter fputs(buf, stdout); 20138032Speter (void) xfclose(fp, "savemail transcript", e->e_id); 20238032Speter } 20338032Speter printf("Original message will be saved in dead.letter.\r\n"); 20438032Speter state = ESM_DEADLETTER; 20538032Speter break; 20638032Speter 20738032Speter case ESM_MAIL: 20838032Speter /* 20938032Speter ** If mailing back, do it. 21038032Speter ** Throw away all further output. Don't alias, 21138032Speter ** since this could cause loops, e.g., if joe 21238032Speter ** mails to joe@x, and for some reason the network 21338032Speter ** for @x is down, then the response gets sent to 21438032Speter ** joe@x, which gives a response, etc. Also force 21538032Speter ** the mail to be delivered even if a version of 21638032Speter ** it has already been sent to the sender. 21738032Speter ** 21838032Speter ** If this is a configuration or local software 21938032Speter ** error, send to the local postmaster as well, 22038032Speter ** since the originator can't do anything 22138032Speter ** about it anyway. Note that this is a full 22238032Speter ** copy of the message (intentionally) so that 22338032Speter ** the Postmaster can forward things along. 22438032Speter */ 22538032Speter 22638032Speter if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE) 22738032Speter { 22838032Speter (void) sendtolist("postmaster", 22938032Speter NULLADDR, &e->e_errorqueue, 0, e); 23038032Speter } 23138032Speter if (!emptyaddr(&e->e_from)) 23238032Speter { 23338032Speter char from[TOBUFSIZE]; 23438032Speter extern bool pruneroute __P((char *)); 23538032Speter 23638032Speter if (strlen(e->e_from.q_paddr) + 1 > sizeof from) 23738032Speter { 23838032Speter state = ESM_POSTMASTER; 23938032Speter break; 24038032Speter } 24138032Speter strcpy(from, e->e_from.q_paddr); 24238032Speter 24338032Speter if (!DontPruneRoutes && pruneroute(from)) 24438032Speter { 24538032Speter ADDRESS *a; 24638032Speter 24738032Speter for (a = e->e_errorqueue; a != NULL; 24838032Speter a = a->q_next) 24938032Speter { 25038032Speter if (sameaddr(a, &e->e_from)) 25138032Speter a->q_flags |= QDONTSEND; 25238032Speter } 25338032Speter } 25438032Speter (void) sendtolist(from, NULLADDR, 25538032Speter &e->e_errorqueue, 0, e); 25638032Speter } 25738032Speter 25838032Speter /* 25938032Speter ** Deliver a non-delivery report to the 26038032Speter ** Postmaster-designate (not necessarily 26138032Speter ** Postmaster). This does not include the 26238032Speter ** body of the message, for privacy reasons. 26338032Speter ** You really shouldn't need this. 26438032Speter */ 26538032Speter 26638032Speter e->e_flags |= EF_PM_NOTIFY; 26738032Speter 26838032Speter /* check to see if there are any good addresses */ 26938032Speter for (q = e->e_errorqueue; q != NULL; q = q->q_next) 27038032Speter if (!bitset(QBADADDR|QDONTSEND, q->q_flags)) 27138032Speter break; 27238032Speter if (q == NULL) 27338032Speter { 27438032Speter /* this is an error-error */ 27538032Speter state = ESM_POSTMASTER; 27638032Speter break; 27738032Speter } 27838032Speter if (returntosender(e->e_message, e->e_errorqueue, 27938032Speter sendbody ? RTSF_SEND_BODY 28038032Speter : RTSF_NO_BODY, 28138032Speter e) == 0) 28238032Speter { 28338032Speter state = ESM_DONE; 28438032Speter break; 28538032Speter } 28638032Speter 28738032Speter /* didn't work -- return to postmaster */ 28838032Speter state = ESM_POSTMASTER; 28938032Speter break; 29038032Speter 29138032Speter case ESM_POSTMASTER: 29238032Speter /* 29338032Speter ** Similar to previous case, but to system postmaster. 29438032Speter */ 29538032Speter 29638032Speter q = NULL; 29738032Speter if (sendtolist(DoubleBounceAddr, 29838032Speter NULLADDR, &q, 0, e) <= 0) 29938032Speter { 30038032Speter syserr("553 cannot parse %s!", DoubleBounceAddr); 30138032Speter ExitStat = EX_SOFTWARE; 30238032Speter state = ESM_USRTMP; 30338032Speter break; 30438032Speter } 30538032Speter flags = RTSF_PM_BOUNCE; 30638032Speter if (sendbody) 30738032Speter flags |= RTSF_SEND_BODY; 30838032Speter if (returntosender(e->e_message, q, flags, e) == 0) 30938032Speter { 31038032Speter state = ESM_DONE; 31138032Speter break; 31238032Speter } 31338032Speter 31438032Speter /* didn't work -- last resort */ 31538032Speter state = ESM_USRTMP; 31638032Speter break; 31738032Speter 31838032Speter case ESM_DEADLETTER: 31938032Speter /* 32038032Speter ** Save the message in dead.letter. 32138032Speter ** If we weren't mailing back, and the user is 32238032Speter ** local, we should save the message in 32338032Speter ** ~/dead.letter so that the poor person doesn't 32438032Speter ** have to type it over again -- and we all know 32538032Speter ** what poor typists UNIX users are. 32638032Speter */ 32738032Speter 32838032Speter p = NULL; 32938032Speter if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags)) 33038032Speter { 33138032Speter if (e->e_from.q_home != NULL) 33238032Speter p = e->e_from.q_home; 33338032Speter else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL) 33438032Speter p = pw->pw_dir; 33538032Speter } 33638032Speter if (p == NULL || e->e_dfp == NULL) 33738032Speter { 33838032Speter /* no local directory or no data file */ 33938032Speter state = ESM_MAIL; 34038032Speter break; 34138032Speter } 34238032Speter 34338032Speter /* we have a home directory; write dead.letter */ 34438032Speter define('z', p, e); 34538032Speter expand("\201z/dead.letter", buf, sizeof buf, e); 34638032Speter flags = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; 34738032Speter if (RealUid == 0) 34838032Speter flags |= SFF_ROOTOK; 34938032Speter e->e_to = buf; 35038032Speter if (mailfile(buf, FileMailer, NULL, flags, e) == EX_OK) 35138032Speter { 35238032Speter int oldverb = Verbose; 35338032Speter 35438032Speter Verbose = 1; 35538032Speter message("Saved message in %s", buf); 35638032Speter Verbose = oldverb; 35738032Speter state = ESM_DONE; 35838032Speter break; 35938032Speter } 36038032Speter state = ESM_MAIL; 36138032Speter break; 36238032Speter 36338032Speter case ESM_USRTMP: 36438032Speter /* 36538032Speter ** Log the mail in /usr/tmp/dead.letter. 36638032Speter */ 36738032Speter 36838032Speter if (e->e_class < 0) 36938032Speter { 37038032Speter state = ESM_DONE; 37138032Speter break; 37238032Speter } 37338032Speter 37438032Speter if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') || 37538032Speter DeadLetterDrop == NULL || DeadLetterDrop[0] == '\0') 37638032Speter { 37738032Speter state = ESM_PANIC; 37838032Speter break; 37938032Speter } 38038032Speter 38138032Speter flags = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN; 38238032Speter if (!writable(DeadLetterDrop, NULL, flags) || 38338032Speter (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND, 38438032Speter FileMode, flags)) == NULL) 38538032Speter { 38638032Speter state = ESM_PANIC; 38738032Speter break; 38838032Speter } 38938032Speter 39038032Speter bzero(&mcibuf, sizeof mcibuf); 39138032Speter mcibuf.mci_out = fp; 39238032Speter mcibuf.mci_mailer = FileMailer; 39338032Speter if (bitnset(M_7BITS, FileMailer->m_flags)) 39438032Speter mcibuf.mci_flags |= MCIF_7BIT; 39538032Speter mcibuf.mci_contentlen = 0; 39638032Speter 39738032Speter putfromline(&mcibuf, e); 39843733Speter (*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER); 39938032Speter (*e->e_putbody)(&mcibuf, e, NULL); 40038032Speter putline("\n", &mcibuf); 40138032Speter (void) fflush(fp); 40238032Speter if (ferror(fp)) 40338032Speter state = ESM_PANIC; 40438032Speter else 40538032Speter { 40638032Speter int oldverb = Verbose; 40738032Speter 40838032Speter Verbose = 1; 40938032Speter message("Saved message in %s", DeadLetterDrop); 41038032Speter Verbose = oldverb; 41138032Speter if (LogLevel > 3) 41238032Speter sm_syslog(LOG_NOTICE, e->e_id, 41338032Speter "Saved message in %s", 41438032Speter DeadLetterDrop); 41538032Speter state = ESM_DONE; 41638032Speter } 41738032Speter (void) xfclose(fp, "savemail", DeadLetterDrop); 41838032Speter break; 41938032Speter 42038032Speter default: 42138032Speter syserr("554 savemail: unknown state %d", state); 42238032Speter 42338032Speter /* fall through ... */ 42438032Speter 42538032Speter case ESM_PANIC: 42638032Speter /* leave the locked queue & transcript files around */ 42738032Speter loseqfile(e, "savemail panic"); 42838032Speter syserr("!554 savemail: cannot save rejected email anywhere"); 42938032Speter } 43038032Speter } 43138032Speter} 43238032Speter/* 43338032Speter** RETURNTOSENDER -- return a message to the sender with an error. 43438032Speter** 43538032Speter** Parameters: 43638032Speter** msg -- the explanatory message. 43738032Speter** returnq -- the queue of people to send the message to. 43838032Speter** flags -- flags tweaking the operation: 43938032Speter** RTSF_SENDBODY -- include body of message (otherwise 44038032Speter** just send the header). 44138032Speter** RTSF_PMBOUNCE -- this is a postmaster bounce. 44238032Speter** e -- the current envelope. 44338032Speter** 44438032Speter** Returns: 44538032Speter** zero -- if everything went ok. 44638032Speter** else -- some error. 44738032Speter** 44838032Speter** Side Effects: 44938032Speter** Returns the current message to the sender via 45038032Speter** mail. 45138032Speter*/ 45238032Speter 45338032Speter#define MAXRETURNS 6 /* max depth of returning messages */ 45438032Speter#define ERRORFUDGE 100 /* nominal size of error message text */ 45538032Speter 45638032Speterint 45738032Speterreturntosender(msg, returnq, flags, e) 45838032Speter char *msg; 45938032Speter ADDRESS *returnq; 46038032Speter int flags; 46138032Speter register ENVELOPE *e; 46238032Speter{ 46338032Speter register ENVELOPE *ee; 46438032Speter ENVELOPE *oldcur = CurEnv; 46538032Speter ENVELOPE errenvelope; 46638032Speter static int returndepth = 0; 46738032Speter register ADDRESS *q; 46838032Speter char *p; 46938032Speter char buf[MAXNAME + 1]; 47038032Speter extern void errbody __P((MCI *, ENVELOPE *, char *)); 47138032Speter 47238032Speter if (returnq == NULL) 47338032Speter return (-1); 47438032Speter 47538032Speter if (msg == NULL) 47638032Speter msg = "Unable to deliver mail"; 47738032Speter 47838032Speter if (tTd(6, 1)) 47938032Speter { 48038032Speter printf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%lx, returnq=", 48138032Speter msg, returndepth, (u_long) e); 48238032Speter printaddr(returnq, TRUE); 48338032Speter if (tTd(6, 20)) 48438032Speter { 48538032Speter printf("Sendq="); 48638032Speter printaddr(e->e_sendqueue, TRUE); 48738032Speter } 48838032Speter } 48938032Speter 49038032Speter if (++returndepth >= MAXRETURNS) 49138032Speter { 49238032Speter if (returndepth != MAXRETURNS) 49338032Speter syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr); 49438032Speter /* don't "unrecurse" and fake a clean exit */ 49538032Speter /* returndepth--; */ 49638032Speter return (0); 49738032Speter } 49838032Speter 49938032Speter define('g', e->e_from.q_paddr, e); 50038032Speter define('u', NULL, e); 50138032Speter 50238032Speter /* initialize error envelope */ 50338032Speter ee = newenvelope(&errenvelope, e); 50438032Speter define('a', "\201b", ee); 50538032Speter define('r', "internal", ee); 50638032Speter define('s', "localhost", ee); 50738032Speter define('_', "localhost", ee); 50838032Speter ee->e_puthdr = putheader; 50938032Speter ee->e_putbody = errbody; 51038032Speter ee->e_flags |= EF_RESPONSE|EF_METOO; 51138032Speter if (!bitset(EF_OLDSTYLE, e->e_flags)) 51238032Speter ee->e_flags &= ~EF_OLDSTYLE; 51338032Speter ee->e_sendqueue = returnq; 51438032Speter ee->e_msgsize = ERRORFUDGE; 51538032Speter if (bitset(RTSF_SEND_BODY, flags)) 51638032Speter ee->e_msgsize += e->e_msgsize; 51738032Speter else 51838032Speter ee->e_flags |= EF_NO_BODY_RETN; 51938032Speter initsys(ee); 52038032Speter for (q = returnq; q != NULL; q = q->q_next) 52138032Speter { 52238032Speter if (bitset(QBADADDR, q->q_flags)) 52338032Speter continue; 52438032Speter 52538032Speter q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 52638032Speter q->q_flags |= QPINGONFAILURE; 52738032Speter 52838032Speter if (!bitset(QDONTSEND, q->q_flags)) 52938032Speter ee->e_nrcpts++; 53038032Speter 53138032Speter if (q->q_alias == NULL) 53238032Speter addheader("To", q->q_paddr, &ee->e_header); 53338032Speter } 53438032Speter 53538032Speter if (LogLevel > 5) 53638032Speter { 53738032Speter if (bitset(EF_RESPONSE|EF_WARNING, e->e_flags)) 53838032Speter p = "return to sender"; 53938032Speter else if (bitset(RTSF_PM_BOUNCE, flags)) 54038032Speter p = "postmaster notify"; 54138032Speter else 54238032Speter p = "DSN"; 54338032Speter sm_syslog(LOG_INFO, e->e_id, 54438032Speter "%s: %s: %s", 54538032Speter ee->e_id, p, shortenstring(msg, MAXSHORTSTR)); 54638032Speter } 54738032Speter 54838032Speter if (SendMIMEErrors) 54938032Speter { 55038032Speter addheader("MIME-Version", "1.0", &ee->e_header); 55138032Speter 55238032Speter (void) snprintf(buf, sizeof buf, "%s.%ld/%.100s", 55340497Sbde ee->e_id, (long)curtime(), MyHostName); 55438032Speter ee->e_msgboundary = newstr(buf); 55538032Speter (void) snprintf(buf, sizeof buf, 55638032Speter#if DSN 55738032Speter "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"", 55838032Speter#else 55938032Speter "multipart/mixed; boundary=\"%s\"", 56038032Speter#endif 56138032Speter ee->e_msgboundary); 56238032Speter addheader("Content-Type", buf, &ee->e_header); 56338032Speter 56438032Speter p = hvalue("Content-Transfer-Encoding", e->e_header); 56538032Speter if (p != NULL && strcasecmp(p, "binary") != 0) 56638032Speter p = NULL; 56738032Speter if (p == NULL && bitset(EF_HAS8BIT, e->e_flags)) 56838032Speter p = "8bit"; 56938032Speter if (p != NULL) 57038032Speter addheader("Content-Transfer-Encoding", p, &ee->e_header); 57138032Speter } 57238032Speter if (strncmp(msg, "Warning:", 8) == 0) 57338032Speter { 57438032Speter addheader("Subject", msg, &ee->e_header); 57538032Speter p = "warning-timeout"; 57638032Speter } 57738032Speter else if (strncmp(msg, "Postmaster warning:", 19) == 0) 57838032Speter { 57938032Speter addheader("Subject", msg, &ee->e_header); 58038032Speter p = "postmaster-warning"; 58138032Speter } 58238032Speter else if (strcmp(msg, "Return receipt") == 0) 58338032Speter { 58438032Speter addheader("Subject", msg, &ee->e_header); 58538032Speter p = "return-receipt"; 58638032Speter } 58738032Speter else if (bitset(RTSF_PM_BOUNCE, flags)) 58838032Speter { 58938032Speter snprintf(buf, sizeof buf, "Postmaster notify: %.*s", 59040497Sbde (int)sizeof buf - 20, msg); 59138032Speter addheader("Subject", buf, &ee->e_header); 59238032Speter p = "postmaster-notification"; 59338032Speter } 59438032Speter else 59538032Speter { 59638032Speter snprintf(buf, sizeof buf, "Returned mail: %.*s", 59740497Sbde (int)sizeof buf - 20, msg); 59838032Speter addheader("Subject", buf, &ee->e_header); 59938032Speter p = "failure"; 60038032Speter } 60138032Speter (void) snprintf(buf, sizeof buf, "auto-generated (%s)", p); 60238032Speter addheader("Auto-Submitted", buf, &ee->e_header); 60338032Speter 60438032Speter /* fake up an address header for the from person */ 60538032Speter expand("\201n", buf, sizeof buf, e); 60638032Speter if (parseaddr(buf, &ee->e_from, RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL) 60738032Speter { 60838032Speter syserr("553 Can't parse myself!"); 60938032Speter ExitStat = EX_SOFTWARE; 61038032Speter returndepth--; 61138032Speter return (-1); 61238032Speter } 61338032Speter ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 61438032Speter ee->e_from.q_flags |= QPINGONFAILURE; 61538032Speter ee->e_sender = ee->e_from.q_paddr; 61638032Speter 61738032Speter /* push state into submessage */ 61838032Speter CurEnv = ee; 61938032Speter define('f', "\201n", ee); 62038032Speter define('x', "Mail Delivery Subsystem", ee); 62138032Speter eatheader(ee, TRUE); 62238032Speter 62338032Speter /* mark statistics */ 62438032Speter markstats(ee, NULLADDR, FALSE); 62538032Speter 62638032Speter /* actually deliver the error message */ 62738032Speter sendall(ee, SM_DELIVER); 62838032Speter 62938032Speter /* restore state */ 63038032Speter dropenvelope(ee, TRUE); 63138032Speter CurEnv = oldcur; 63238032Speter returndepth--; 63338032Speter 63438032Speter /* check for delivery errors */ 63538032Speter if (ee->e_parent == NULL || !bitset(EF_RESPONSE, ee->e_parent->e_flags)) 63638032Speter return 0; 63738032Speter for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 63838032Speter { 63938032Speter if (bitset(QQUEUEUP|QSENT, q->q_flags)) 64038032Speter return 0; 64138032Speter } 64238032Speter return -1; 64338032Speter} 64438032Speter/* 64538032Speter** ERRBODY -- output the body of an error message. 64638032Speter** 64738032Speter** Typically this is a copy of the transcript plus a copy of the 64838032Speter** original offending message. 64938032Speter** 65038032Speter** Parameters: 65138032Speter** mci -- the mailer connection information. 65238032Speter** e -- the envelope we are working in. 65338032Speter** separator -- any possible MIME separator. 65438032Speter** 65538032Speter** Returns: 65638032Speter** none 65738032Speter** 65838032Speter** Side Effects: 65938032Speter** Outputs the body of an error message. 66038032Speter*/ 66138032Speter 66238032Spetervoid 66338032Spetererrbody(mci, e, separator) 66438032Speter register MCI *mci; 66538032Speter register ENVELOPE *e; 66638032Speter char *separator; 66738032Speter{ 66838032Speter register FILE *xfile; 66938032Speter char *p; 67038032Speter register ADDRESS *q = NULL; 67138032Speter bool printheader; 67238032Speter bool sendbody; 67338032Speter bool pm_notify; 67438032Speter char buf[MAXLINE]; 67538032Speter 67638032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 67738032Speter { 67838032Speter putline("", mci); 67938032Speter mci->mci_flags &= ~MCIF_INHEADER; 68038032Speter } 68138032Speter if (e->e_parent == NULL) 68238032Speter { 68338032Speter syserr("errbody: null parent"); 68438032Speter putline(" ----- Original message lost -----\n", mci); 68538032Speter return; 68638032Speter } 68738032Speter 68838032Speter /* 68938032Speter ** Output MIME header. 69038032Speter */ 69138032Speter 69238032Speter if (e->e_msgboundary != NULL) 69338032Speter { 69438032Speter putline("This is a MIME-encapsulated message", mci); 69538032Speter putline("", mci); 69638032Speter (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); 69738032Speter putline(buf, mci); 69838032Speter putline("", mci); 69938032Speter } 70038032Speter 70138032Speter /* 70238032Speter ** Output introductory information. 70338032Speter */ 70438032Speter 70538032Speter pm_notify = FALSE; 70638032Speter p = hvalue("subject", e->e_header); 70738032Speter if (p != NULL && strncmp(p, "Postmaster ", 11) == 0) 70838032Speter pm_notify = TRUE; 70938032Speter else 71038032Speter { 71138032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 71238032Speter if (bitset(QBADADDR, q->q_flags)) 71338032Speter break; 71438032Speter } 71538032Speter if (!pm_notify && q == NULL && 71638032Speter !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags)) 71738032Speter { 71838032Speter putline(" **********************************************", 71938032Speter mci); 72038032Speter putline(" ** THIS IS A WARNING MESSAGE ONLY **", 72138032Speter mci); 72238032Speter putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **", 72338032Speter mci); 72438032Speter putline(" **********************************************", 72538032Speter mci); 72638032Speter putline("", mci); 72738032Speter } 72838032Speter snprintf(buf, sizeof buf, "The original message was received at %s", 72938032Speter arpadate(ctime(&e->e_parent->e_ctime))); 73038032Speter putline(buf, mci); 73138032Speter expand("from \201_", buf, sizeof buf, e->e_parent); 73238032Speter putline(buf, mci); 73338032Speter putline("", mci); 73438032Speter 73538032Speter /* 73638032Speter ** Output error message header (if specified and available). 73738032Speter */ 73838032Speter 73938032Speter if (ErrMsgFile != NULL && !bitset(EF_SENDRECEIPT, e->e_parent->e_flags)) 74038032Speter { 74138032Speter if (*ErrMsgFile == '/') 74238032Speter { 74338032Speter int sff = SFF_ROOTOK|SFF_REGONLY; 74438032Speter 74538032Speter if (DontLockReadFiles) 74638032Speter sff |= SFF_NOLOCK; 74738032Speter if (!bitset(DBS_ERRORHEADERINUNSAFEDIRPATH, DontBlameSendmail)) 74838032Speter sff |= SFF_SAFEDIRPATH; 74938032Speter xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff); 75038032Speter if (xfile != NULL) 75138032Speter { 75238032Speter while (fgets(buf, sizeof buf, xfile) != NULL) 75338032Speter { 75438032Speter extern void translate_dollars __P((char *)); 75538032Speter 75638032Speter translate_dollars(buf); 75738032Speter expand(buf, buf, sizeof buf, e); 75838032Speter putline(buf, mci); 75938032Speter } 76038032Speter (void) fclose(xfile); 76138032Speter putline("\n", mci); 76238032Speter } 76338032Speter } 76438032Speter else 76538032Speter { 76638032Speter expand(ErrMsgFile, buf, sizeof buf, e); 76738032Speter putline(buf, mci); 76838032Speter putline("", mci); 76938032Speter } 77038032Speter } 77138032Speter 77238032Speter /* 77338032Speter ** Output message introduction 77438032Speter */ 77538032Speter 77638032Speter printheader = TRUE; 77738032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 77838032Speter { 77938032Speter if (!bitset(QBADADDR, q->q_flags) || 78038032Speter !bitset(QPINGONFAILURE, q->q_flags)) 78138032Speter continue; 78238032Speter 78338032Speter if (printheader) 78438032Speter { 78538032Speter putline(" ----- The following addresses had permanent fatal errors -----", 78638032Speter mci); 78738032Speter printheader = FALSE; 78838032Speter } 78938032Speter 79038032Speter snprintf(buf, sizeof buf, "%s", 79138032Speter shortenstring(q->q_paddr, MAXSHORTSTR)); 79238032Speter putline(buf, mci); 79338032Speter if (q->q_alias != NULL) 79438032Speter { 79538032Speter snprintf(buf, sizeof buf, " (expanded from: %s)", 79638032Speter shortenstring(q->q_alias->q_paddr, MAXSHORTSTR)); 79738032Speter putline(buf, mci); 79838032Speter } 79938032Speter } 80038032Speter if (!printheader) 80138032Speter putline("", mci); 80238032Speter 80338032Speter printheader = TRUE; 80438032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 80538032Speter { 80638032Speter if (bitset(QBADADDR, q->q_flags) || 80738032Speter !bitset(QPRIMARY, q->q_flags) || 80838032Speter !bitset(QDELAYED, q->q_flags)) 80938032Speter continue; 81038032Speter 81138032Speter if (printheader) 81238032Speter { 81338032Speter putline(" ----- The following addresses had transient non-fatal errors -----", 81438032Speter mci); 81538032Speter printheader = FALSE; 81638032Speter } 81738032Speter 81838032Speter snprintf(buf, sizeof buf, "%s", 81938032Speter shortenstring(q->q_paddr, MAXSHORTSTR)); 82038032Speter putline(buf, mci); 82138032Speter if (q->q_alias != NULL) 82238032Speter { 82338032Speter snprintf(buf, sizeof buf, " (expanded from: %s)", 82438032Speter shortenstring(q->q_alias->q_paddr, MAXSHORTSTR)); 82538032Speter putline(buf, mci); 82638032Speter } 82738032Speter } 82838032Speter if (!printheader) 82938032Speter putline("", mci); 83038032Speter 83138032Speter printheader = TRUE; 83238032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 83338032Speter { 83438032Speter if (bitset(QBADADDR, q->q_flags) || 83538032Speter !bitset(QPRIMARY, q->q_flags) || 83638032Speter bitset(QDELAYED, q->q_flags)) 83738032Speter continue; 83838032Speter else if (!bitset(QPINGONSUCCESS, q->q_flags)) 83938032Speter continue; 84038032Speter else if (bitset(QRELAYED, q->q_flags)) 84138032Speter p = "relayed to non-DSN-aware mailer"; 84238032Speter else if (bitset(QDELIVERED, q->q_flags)) 84338032Speter { 84438032Speter if (bitset(QEXPANDED, q->q_flags)) 84538032Speter p = "successfully delivered to mailing list"; 84638032Speter else 84738032Speter p = "successfully delivered to mailbox"; 84838032Speter } 84938032Speter else if (bitset(QEXPANDED, q->q_flags)) 85038032Speter p = "expanded by alias"; 85138032Speter else 85238032Speter continue; 85338032Speter 85438032Speter if (printheader) 85538032Speter { 85638032Speter putline(" ----- The following addresses had successful delivery notifications -----", 85738032Speter mci); 85838032Speter printheader = FALSE; 85938032Speter } 86038032Speter 86138032Speter snprintf(buf, sizeof buf, "%s (%s)", 86238032Speter shortenstring(q->q_paddr, MAXSHORTSTR), p); 86338032Speter putline(buf, mci); 86438032Speter if (q->q_alias != NULL) 86538032Speter { 86638032Speter snprintf(buf, sizeof buf, " (expanded from: %s)", 86738032Speter shortenstring(q->q_alias->q_paddr, MAXSHORTSTR)); 86838032Speter putline(buf, mci); 86938032Speter } 87038032Speter } 87138032Speter if (!printheader) 87238032Speter putline("", mci); 87338032Speter 87438032Speter /* 87538032Speter ** Output transcript of errors 87638032Speter */ 87738032Speter 87838032Speter (void) fflush(stdout); 87938032Speter p = queuename(e->e_parent, 'x'); 88038032Speter if ((xfile = fopen(p, "r")) == NULL) 88138032Speter { 88238032Speter syserr("Cannot open %s", p); 88338032Speter putline(" ----- Transcript of session is unavailable -----\n", mci); 88438032Speter } 88538032Speter else 88638032Speter { 88738032Speter printheader = TRUE; 88838032Speter if (e->e_xfp != NULL) 88938032Speter (void) fflush(e->e_xfp); 89038032Speter while (fgets(buf, sizeof buf, xfile) != NULL) 89138032Speter { 89238032Speter if (printheader) 89338032Speter putline(" ----- Transcript of session follows -----\n", mci); 89438032Speter printheader = FALSE; 89538032Speter putline(buf, mci); 89638032Speter } 89738032Speter (void) xfclose(xfile, "errbody xscript", p); 89838032Speter } 89938032Speter errno = 0; 90038032Speter 90138032Speter#if DSN 90238032Speter /* 90338032Speter ** Output machine-readable version. 90438032Speter */ 90538032Speter 90638032Speter if (e->e_msgboundary != NULL) 90738032Speter { 90838032Speter putline("", mci); 90938032Speter (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); 91038032Speter putline(buf, mci); 91138032Speter putline("Content-Type: message/delivery-status", mci); 91238032Speter putline("", mci); 91338032Speter 91438032Speter /* 91538032Speter ** Output per-message information. 91638032Speter */ 91738032Speter 91838032Speter /* original envelope id from MAIL FROM: line */ 91938032Speter if (e->e_parent->e_envid != NULL) 92038032Speter { 92138032Speter (void) snprintf(buf, sizeof buf, "Original-Envelope-Id: %.800s", 92238032Speter xuntextify(e->e_parent->e_envid)); 92338032Speter putline(buf, mci); 92438032Speter } 92538032Speter 92638032Speter /* Reporting-MTA: is us (required) */ 92738032Speter (void) snprintf(buf, sizeof buf, "Reporting-MTA: dns; %.800s", MyHostName); 92838032Speter putline(buf, mci); 92938032Speter 93038032Speter /* DSN-Gateway: not relevant since we are not translating */ 93138032Speter 93238032Speter /* Received-From-MTA: shows where we got this message from */ 93338032Speter if (RealHostName != NULL) 93438032Speter { 93538032Speter /* XXX use $s for type? */ 93638032Speter if (e->e_parent->e_from.q_mailer == NULL || 93738032Speter (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL) 93838032Speter p = "dns"; 93938032Speter (void) snprintf(buf, sizeof buf, "Received-From-MTA: %s; %.800s", 94038032Speter p, RealHostName); 94138032Speter putline(buf, mci); 94238032Speter } 94338032Speter 94438032Speter /* Arrival-Date: -- when it arrived here */ 94538032Speter (void) snprintf(buf, sizeof buf, "Arrival-Date: %s", 94638032Speter arpadate(ctime(&e->e_parent->e_ctime))); 94738032Speter putline(buf, mci); 94838032Speter 94938032Speter /* 95038032Speter ** Output per-address information. 95138032Speter */ 95238032Speter 95338032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 95438032Speter { 95538032Speter register ADDRESS *r; 95638032Speter char *action; 95738032Speter 95838032Speter if (bitset(QBADADDR, q->q_flags)) 95938032Speter action = "failed"; 96038032Speter else if (!bitset(QPRIMARY, q->q_flags)) 96138032Speter continue; 96238032Speter else if (bitset(QDELIVERED, q->q_flags)) 96338032Speter { 96438032Speter if (bitset(QEXPANDED, q->q_flags)) 96538032Speter action = "delivered (to mailing list)"; 96638032Speter else 96738032Speter action = "delivered (to mailbox)"; 96838032Speter } 96938032Speter else if (bitset(QRELAYED, q->q_flags)) 97038032Speter action = "relayed (to non-DSN-aware mailer)"; 97138032Speter else if (bitset(QEXPANDED, q->q_flags)) 97238032Speter action = "expanded (to multi-recipient alias)"; 97338032Speter else if (bitset(QDELAYED, q->q_flags)) 97438032Speter action = "delayed"; 97538032Speter else 97638032Speter continue; 97738032Speter 97838032Speter putline("", mci); 97938032Speter 98038032Speter /* Original-Recipient: -- passed from on high */ 98138032Speter if (q->q_orcpt != NULL) 98238032Speter { 98338032Speter (void) snprintf(buf, sizeof buf, "Original-Recipient: %.800s", 98438032Speter q->q_orcpt); 98538032Speter putline(buf, mci); 98638032Speter } 98738032Speter 98838032Speter /* Final-Recipient: -- the name from the RCPT command */ 98938032Speter p = e->e_parent->e_from.q_mailer->m_addrtype; 99038032Speter if (p == NULL) 99138032Speter p = "rfc822"; 99238032Speter for (r = q; r->q_alias != NULL; r = r->q_alias) 99338032Speter continue; 99438032Speter if (strchr(r->q_user, '@') != NULL) 99538032Speter { 99638032Speter (void) snprintf(buf, sizeof buf, 99738032Speter "Final-Recipient: %s; %.800s", 99838032Speter p, r->q_user); 99938032Speter } 100038032Speter else if (strchr(r->q_paddr, '@') != NULL) 100138032Speter { 100238032Speter (void) snprintf(buf, sizeof buf, 100338032Speter "Final-Recipient: %s; %.800s", 100438032Speter p, r->q_paddr); 100538032Speter } 100638032Speter else 100738032Speter { 100838032Speter (void) snprintf(buf, sizeof buf, 100938032Speter "Final-Recipient: %s; %.700s@%.100s", 101038032Speter p, r->q_user, MyHostName); 101138032Speter } 101238032Speter putline(buf, mci); 101338032Speter 101438032Speter /* X-Actual-Recipient: -- the real problem address */ 101538032Speter if (r != q && q->q_user[0] != '\0') 101638032Speter { 101738032Speter if (strchr(q->q_user, '@') == NULL) 101838032Speter { 101938032Speter (void) snprintf(buf, sizeof buf, 102038032Speter "X-Actual-Recipient: %s; %.700s@%.100s", 102138032Speter p, q->q_user, MyHostName); 102238032Speter } 102338032Speter else 102438032Speter { 102538032Speter (void) snprintf(buf, sizeof buf, 102638032Speter "X-Actual-Recipient: %s; %.800s", 102738032Speter p, q->q_user); 102838032Speter } 102938032Speter putline(buf, mci); 103038032Speter } 103138032Speter 103238032Speter /* Action: -- what happened? */ 103338032Speter snprintf(buf, sizeof buf, "Action: %s", action); 103438032Speter putline(buf, mci); 103538032Speter 103638032Speter /* Status: -- what _really_ happened? */ 103738032Speter if (q->q_status != NULL) 103838032Speter p = q->q_status; 103938032Speter else if (bitset(QBADADDR, q->q_flags)) 104038032Speter p = "5.0.0"; 104138032Speter else if (bitset(QQUEUEUP, q->q_flags)) 104238032Speter p = "4.0.0"; 104338032Speter else 104438032Speter p = "2.0.0"; 104538032Speter snprintf(buf, sizeof buf, "Status: %s", p); 104638032Speter putline(buf, mci); 104738032Speter 104838032Speter /* Remote-MTA: -- who was I talking to? */ 104938032Speter if (q->q_statmta != NULL) 105038032Speter { 105138032Speter if (q->q_mailer == NULL || 105238032Speter (p = q->q_mailer->m_mtatype) == NULL) 105338032Speter p = "dns"; 105438032Speter (void) snprintf(buf, sizeof buf, 105538032Speter "Remote-MTA: %s; %.800s", 105638032Speter p, q->q_statmta); 105738032Speter p = &buf[strlen(buf) - 1]; 105838032Speter if (*p == '.') 105938032Speter *p = '\0'; 106038032Speter putline(buf, mci); 106138032Speter } 106238032Speter 106338032Speter /* Diagnostic-Code: -- actual result from other end */ 106438032Speter if (q->q_rstatus != NULL) 106538032Speter { 106638032Speter p = q->q_mailer->m_diagtype; 106738032Speter if (p == NULL) 106838032Speter p = "smtp"; 106938032Speter (void) snprintf(buf, sizeof buf, 107038032Speter "Diagnostic-Code: %s; %.800s", 107138032Speter p, q->q_rstatus); 107238032Speter putline(buf, mci); 107338032Speter } 107438032Speter 107538032Speter /* Last-Attempt-Date: -- fine granularity */ 107638032Speter if (q->q_statdate == (time_t) 0L) 107738032Speter q->q_statdate = curtime(); 107838032Speter (void) snprintf(buf, sizeof buf, 107938032Speter "Last-Attempt-Date: %s", 108038032Speter arpadate(ctime(&q->q_statdate))); 108138032Speter putline(buf, mci); 108238032Speter 108338032Speter /* Will-Retry-Until: -- for delayed messages only */ 108438032Speter if (bitset(QQUEUEUP, q->q_flags) && 108538032Speter !bitset(QBADADDR, q->q_flags)) 108638032Speter { 108738032Speter time_t xdate; 108838032Speter 108938032Speter xdate = e->e_parent->e_ctime + 109038032Speter TimeOuts.to_q_return[e->e_parent->e_timeoutclass]; 109138032Speter snprintf(buf, sizeof buf, 109238032Speter "Will-Retry-Until: %s", 109338032Speter arpadate(ctime(&xdate))); 109438032Speter putline(buf, mci); 109538032Speter } 109638032Speter } 109738032Speter } 109838032Speter#endif 109938032Speter 110038032Speter /* 110138032Speter ** Output text of original message 110238032Speter */ 110338032Speter 110438032Speter putline("", mci); 110538032Speter if (bitset(EF_HAS_DF, e->e_parent->e_flags)) 110638032Speter { 110738032Speter sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) && 110838032Speter !bitset(EF_NO_BODY_RETN, e->e_flags); 110938032Speter 111038032Speter if (e->e_msgboundary == NULL) 111138032Speter { 111238032Speter if (sendbody) 111338032Speter putline(" ----- Original message follows -----\n", mci); 111438032Speter else 111538032Speter putline(" ----- Message header follows -----\n", mci); 111638032Speter (void) fflush(mci->mci_out); 111738032Speter } 111838032Speter else 111938032Speter { 112038032Speter (void) snprintf(buf, sizeof buf, "--%s", 112138032Speter e->e_msgboundary); 112238032Speter 112338032Speter putline(buf, mci); 112438032Speter (void) snprintf(buf, sizeof buf, "Content-Type: %s", 112538032Speter sendbody ? "message/rfc822" 112638032Speter : "text/rfc822-headers"); 112738032Speter putline(buf, mci); 112838032Speter 112938032Speter p = hvalue("Content-Transfer-Encoding", e->e_parent->e_header); 113038032Speter if (p != NULL && strcasecmp(p, "binary") != 0) 113138032Speter p = NULL; 113238032Speter if (p == NULL && bitset(EF_HAS8BIT, e->e_parent->e_flags)) 113338032Speter p = "8bit"; 113438032Speter if (p != NULL) 113538032Speter { 113638032Speter (void) snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %s", 113738032Speter p); 113838032Speter putline(buf, mci); 113938032Speter } 114038032Speter } 114138032Speter putline("", mci); 114243733Speter putheader(mci, e->e_parent->e_header, e->e_parent, M87F_OUTER); 114338032Speter if (sendbody) 114438032Speter putbody(mci, e->e_parent, e->e_msgboundary); 114538032Speter else if (e->e_msgboundary == NULL) 114638032Speter { 114738032Speter putline("", mci); 114838032Speter putline(" ----- Message body suppressed -----", mci); 114938032Speter } 115038032Speter } 115138032Speter else if (e->e_msgboundary == NULL) 115238032Speter { 115338032Speter putline(" ----- No message was collected -----\n", mci); 115438032Speter } 115538032Speter 115638032Speter if (e->e_msgboundary != NULL) 115738032Speter { 115838032Speter putline("", mci); 115938032Speter (void) snprintf(buf, sizeof buf, "--%s--", e->e_msgboundary); 116038032Speter putline(buf, mci); 116138032Speter } 116238032Speter putline("", mci); 116338032Speter 116438032Speter /* 116538032Speter ** Cleanup and exit 116638032Speter */ 116738032Speter 116838032Speter if (errno != 0) 116938032Speter syserr("errbody: I/O error"); 117038032Speter} 117138032Speter/* 117238032Speter** SMTPTODSN -- convert SMTP to DSN status code 117338032Speter** 117438032Speter** Parameters: 117538032Speter** smtpstat -- the smtp status code (e.g., 550). 117638032Speter** 117738032Speter** Returns: 117838032Speter** The DSN version of the status code. 117938032Speter*/ 118038032Speter 118138032Speterchar * 118238032Spetersmtptodsn(smtpstat) 118338032Speter int smtpstat; 118438032Speter{ 118538032Speter if (smtpstat < 0) 118638032Speter return "4.4.2"; 118738032Speter 118838032Speter switch (smtpstat) 118938032Speter { 119038032Speter case 450: /* Req mail action not taken: mailbox unavailable */ 119138032Speter return "4.2.0"; 119238032Speter 119338032Speter case 451: /* Req action aborted: local error in processing */ 119438032Speter return "4.3.0"; 119538032Speter 119638032Speter case 452: /* Req action not taken: insufficient sys storage */ 119738032Speter return "4.3.1"; 119838032Speter 119938032Speter case 500: /* Syntax error, command unrecognized */ 120038032Speter return "5.5.2"; 120138032Speter 120238032Speter case 501: /* Syntax error in parameters or arguments */ 120338032Speter return "5.5.4"; 120438032Speter 120538032Speter case 502: /* Command not implemented */ 120638032Speter return "5.5.1"; 120738032Speter 120838032Speter case 503: /* Bad sequence of commands */ 120938032Speter return "5.5.1"; 121038032Speter 121138032Speter case 504: /* Command parameter not implemented */ 121238032Speter return "5.5.4"; 121338032Speter 121438032Speter case 550: /* Req mail action not taken: mailbox unavailable */ 121538032Speter return "5.2.0"; 121638032Speter 121738032Speter case 551: /* User not local; please try <...> */ 121838032Speter return "5.1.6"; 121938032Speter 122038032Speter case 552: /* Req mail action aborted: exceeded storage alloc */ 122138032Speter return "5.2.2"; 122238032Speter 122338032Speter case 553: /* Req action not taken: mailbox name not allowed */ 122438032Speter return "5.1.0"; 122538032Speter 122638032Speter case 554: /* Transaction failed */ 122738032Speter return "5.0.0"; 122838032Speter } 122938032Speter 123038032Speter if ((smtpstat / 100) == 2) 123138032Speter return "2.0.0"; 123238032Speter if ((smtpstat / 100) == 4) 123338032Speter return "4.0.0"; 123438032Speter return "5.0.0"; 123538032Speter} 123638032Speter/* 123738032Speter** XTEXTIFY -- take regular text and turn it into DSN-style xtext 123838032Speter** 123938032Speter** Parameters: 124038032Speter** t -- the text to convert. 124138032Speter** taboo -- additional characters that must be encoded. 124238032Speter** 124338032Speter** Returns: 124438032Speter** The xtext-ified version of the same string. 124538032Speter*/ 124638032Speter 124738032Speterchar * 124838032Speterxtextify(t, taboo) 124938032Speter register char *t; 125038032Speter char *taboo; 125138032Speter{ 125238032Speter register char *p; 125338032Speter int l; 125438032Speter int nbogus; 125538032Speter static char *bp = NULL; 125638032Speter static int bplen = 0; 125738032Speter 125838032Speter if (taboo == NULL) 125938032Speter taboo = ""; 126038032Speter 126138032Speter /* figure out how long this xtext will have to be */ 126238032Speter nbogus = l = 0; 126338032Speter for (p = t; *p != '\0'; p++) 126438032Speter { 126538032Speter register int c = (*p & 0xff); 126638032Speter 126738032Speter /* ASCII dependence here -- this is the way the spec words it */ 126838032Speter if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || 126938032Speter strchr(taboo, c) != NULL) 127038032Speter nbogus++; 127138032Speter l++; 127238032Speter } 127338032Speter if (nbogus == 0) 127438032Speter return t; 127538032Speter l += nbogus * 2 + 1; 127638032Speter 127738032Speter /* now allocate space if necessary for the new string */ 127838032Speter if (l > bplen) 127938032Speter { 128038032Speter if (bp != NULL) 128138032Speter free(bp); 128238032Speter bp = xalloc(l); 128338032Speter bplen = l; 128438032Speter } 128538032Speter 128638032Speter /* ok, copy the text with byte expansion */ 128738032Speter for (p = bp; *t != '\0'; ) 128838032Speter { 128938032Speter register int c = (*t++ & 0xff); 129038032Speter 129138032Speter /* ASCII dependence here -- this is the way the spec words it */ 129238032Speter if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || 129338032Speter strchr(taboo, c) != NULL) 129438032Speter { 129538032Speter *p++ = '+'; 129638032Speter *p++ = "0123456789abcdef"[c >> 4]; 129738032Speter *p++ = "0123456789abcdef"[c & 0xf]; 129838032Speter } 129938032Speter else 130038032Speter *p++ = c; 130138032Speter } 130238032Speter *p = '\0'; 130338032Speter return bp; 130438032Speter} 130538032Speter/* 130638032Speter** XUNTEXTIFY -- take xtext and turn it into plain text 130738032Speter** 130838032Speter** Parameters: 130938032Speter** t -- the xtextified text. 131038032Speter** 131138032Speter** Returns: 131238032Speter** The decoded text. No attempt is made to deal with 131338032Speter** null strings in the resulting text. 131438032Speter*/ 131538032Speter 131638032Speterchar * 131738032Speterxuntextify(t) 131838032Speter register char *t; 131938032Speter{ 132038032Speter register char *p; 132138032Speter int l; 132238032Speter static char *bp = NULL; 132338032Speter static int bplen = 0; 132438032Speter 132538032Speter /* heuristic -- if no plus sign, just return the input */ 132638032Speter if (strchr(t, '+') == NULL) 132738032Speter return t; 132838032Speter 132938032Speter /* xtext is always longer than decoded text */ 133038032Speter l = strlen(t); 133138032Speter if (l > bplen) 133238032Speter { 133338032Speter if (bp != NULL) 133438032Speter free(bp); 133538032Speter bp = xalloc(l); 133638032Speter bplen = l; 133738032Speter } 133838032Speter 133938032Speter /* ok, copy the text with byte compression */ 134038032Speter for (p = bp; *t != '\0'; t++) 134138032Speter { 134238032Speter register int c = *t & 0xff; 134338032Speter 134438032Speter if (c != '+') 134538032Speter { 134638032Speter *p++ = c; 134738032Speter continue; 134838032Speter } 134938032Speter 135038032Speter c = *++t & 0xff; 135138032Speter if (!isascii(c) || !isxdigit(c)) 135238032Speter { 135338032Speter /* error -- first digit is not hex */ 135438032Speter usrerr("bogus xtext: +%c", c); 135538032Speter t--; 135638032Speter continue; 135738032Speter } 135838032Speter if (isdigit(c)) 135938032Speter c -= '0'; 136038032Speter else if (isupper(c)) 136138032Speter c -= 'A' - 10; 136238032Speter else 136338032Speter c -= 'a' - 10; 136438032Speter *p = c << 4; 136538032Speter 136638032Speter c = *++t & 0xff; 136738032Speter if (!isascii(c) || !isxdigit(c)) 136838032Speter { 136938032Speter /* error -- second digit is not hex */ 137038032Speter usrerr("bogus xtext: +%x%c", *p >> 4, c); 137138032Speter t--; 137238032Speter continue; 137338032Speter } 137438032Speter if (isdigit(c)) 137538032Speter c -= '0'; 137638032Speter else if (isupper(c)) 137738032Speter c -= 'A' - 10; 137838032Speter else 137938032Speter c -= 'a' - 10; 138038032Speter *p++ |= c; 138138032Speter } 138238032Speter *p = '\0'; 138338032Speter return bp; 138438032Speter} 138538032Speter/* 138638032Speter** XTEXTOK -- check if a string is legal xtext 138738032Speter** 138838032Speter** Xtext is used in Delivery Status Notifications. The spec was 138938032Speter** taken from RFC 1891, ``SMTP Service Extension for Delivery 139038032Speter** Status Notifications''. 139138032Speter** 139238032Speter** Parameters: 139338032Speter** s -- the string to check. 139438032Speter** 139538032Speter** Returns: 139638032Speter** TRUE -- if 's' is legal xtext. 139738032Speter** FALSE -- if it has any illegal characters in it. 139838032Speter*/ 139938032Speter 140038032Speterbool 140138032Speterxtextok(s) 140238032Speter char *s; 140338032Speter{ 140438032Speter int c; 140538032Speter 140638032Speter while ((c = *s++) != '\0') 140738032Speter { 140838032Speter if (c == '+') 140938032Speter { 141038032Speter c = *s++; 141138032Speter if (!isascii(c) || !isxdigit(c)) 141238032Speter return FALSE; 141338032Speter c = *s++; 141438032Speter if (!isascii(c) || !isxdigit(c)) 141538032Speter return FALSE; 141638032Speter } 141738032Speter else if (c < '!' || c > '~' || c == '=') 141838032Speter return FALSE; 141938032Speter } 142038032Speter return TRUE; 142138032Speter} 142238032Speter/* 142338032Speter** PRUNEROUTE -- prune an RFC-822 source route 142438032Speter** 142538032Speter** Trims down a source route to the last internet-registered hop. 142638032Speter** This is encouraged by RFC 1123 section 5.3.3. 142738032Speter** 142838032Speter** Parameters: 142938032Speter** addr -- the address 143038032Speter** 143138032Speter** Returns: 143238032Speter** TRUE -- address was modified 143338032Speter** FALSE -- address could not be pruned 143438032Speter** 143538032Speter** Side Effects: 143638032Speter** modifies addr in-place 143738032Speter*/ 143838032Speter 143938032Speterbool 144038032Speterpruneroute(addr) 144138032Speter char *addr; 144238032Speter{ 144338032Speter#if NAMED_BIND 144438032Speter char *start, *at, *comma; 144538032Speter char c; 144638032Speter int rcode; 144738032Speter int i; 144838032Speter char hostbuf[BUFSIZ]; 144938032Speter char *mxhosts[MAXMXHOSTS + 1]; 145038032Speter 145138032Speter /* check to see if this is really a route-addr */ 145238032Speter if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') 145338032Speter return FALSE; 145438032Speter start = strchr(addr, ':'); 145538032Speter at = strrchr(addr, '@'); 145638032Speter if (start == NULL || at == NULL || at < start) 145738032Speter return FALSE; 145838032Speter 145938032Speter /* slice off the angle brackets */ 146038032Speter i = strlen(at + 1); 146138032Speter if (i >= (SIZE_T) sizeof hostbuf) 146238032Speter return FALSE; 146338032Speter strcpy(hostbuf, at + 1); 146438032Speter hostbuf[i - 1] = '\0'; 146538032Speter 146638032Speter while (start) 146738032Speter { 146838032Speter if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0) 146938032Speter { 147038032Speter strcpy(addr + 1, start + 1); 147138032Speter return TRUE; 147238032Speter } 147338032Speter c = *start; 147438032Speter *start = '\0'; 147538032Speter comma = strrchr(addr, ','); 147638032Speter if (comma != NULL && comma[1] == '@' && 147738032Speter strlen(comma + 2) < (SIZE_T) sizeof hostbuf) 147838032Speter strcpy(hostbuf, comma + 2); 147938032Speter else 148038032Speter comma = NULL; 148138032Speter *start = c; 148238032Speter start = comma; 148338032Speter } 148438032Speter#endif 148538032Speter return FALSE; 148638032Speter} 1487