savemail.c revision 159613
138032Speter/* 2159613Sgshapiro * Copyright (c) 1998-2003, 2006 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 * 12157006Sgshapiro * $FreeBSD: head/contrib/sendmail/src/savemail.c 159613 2006-06-14 16:25:31Z gshapiro $ 13157006Sgshapiro * 1438032Speter */ 1538032Speter 1664565Sgshapiro#include <sendmail.h> 1764565Sgshapiro 18159613SgshapiroSM_RCSID("@(#)$Id: savemail.c,v 8.308 2006/04/18 01:31:33 ca Exp $") 1964565Sgshapiro 20157006Sgshapirostatic bool errbody __P((MCI *, ENVELOPE *, char *)); 2164565Sgshapirostatic bool pruneroute __P((char *)); 2264565Sgshapiro 2338032Speter/* 2438032Speter** SAVEMAIL -- Save mail on error 2538032Speter** 2638032Speter** If mailing back errors, mail it back to the originator 2738032Speter** together with an error message; otherwise, just put it in 2838032Speter** dead.letter in the user's home directory (if he exists on 2938032Speter** this machine). 3038032Speter** 3138032Speter** Parameters: 3238032Speter** e -- the envelope containing the message in error. 3390795Sgshapiro** sendbody -- if true, also send back the body of the 3438032Speter** message; otherwise just send the header. 3538032Speter** 3638032Speter** Returns: 3790795Sgshapiro** true if savemail panic'ed, (i.e., the data file should 3890795Sgshapiro** be preserved by dropenvelope()) 3938032Speter** 4038032Speter** Side Effects: 4138032Speter** Saves the letter, by writing or mailing it back to the 4238032Speter** sender, or by putting it in dead.letter in her home 4338032Speter** directory. 4438032Speter*/ 4538032Speter 4638032Speter/* defines for state machine */ 4764565Sgshapiro#define ESM_REPORT 0 /* report to sender's terminal */ 4864565Sgshapiro#define ESM_MAIL 1 /* mail back to sender */ 4964565Sgshapiro#define ESM_QUIET 2 /* mail has already been returned */ 5064565Sgshapiro#define ESM_DEADLETTER 3 /* save in ~/dead.letter */ 5164565Sgshapiro#define ESM_POSTMASTER 4 /* return to postmaster */ 5264565Sgshapiro#define ESM_DEADLETTERDROP 5 /* save in DeadLetterDrop */ 5364565Sgshapiro#define ESM_PANIC 6 /* call loseqfile() */ 5464565Sgshapiro#define ESM_DONE 7 /* message is successfully delivered */ 5538032Speter 5690795Sgshapirobool 5738032Spetersavemail(e, sendbody) 5838032Speter register ENVELOPE *e; 5938032Speter bool sendbody; 6038032Speter{ 6190795Sgshapiro register SM_FILE_T *fp; 6290795Sgshapiro bool panic = false; 6338032Speter int state; 6438032Speter auto ADDRESS *q = NULL; 6538032Speter register char *p; 6638032Speter MCI mcibuf; 6738032Speter int flags; 6864565Sgshapiro long sff; 6964565Sgshapiro char buf[MAXLINE + 1]; 7098125Sgshapiro char dlbuf[MAXPATHLEN]; 7190795Sgshapiro SM_MBDB_T user; 7238032Speter 7390795Sgshapiro 7438032Speter if (tTd(6, 1)) 7538032Speter { 7690795Sgshapiro sm_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); 79132946Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 8038032Speter } 8138032Speter 8238032Speter if (e->e_id == NULL) 8338032Speter { 8438032Speter /* can't return a message with no id */ 8590795Sgshapiro return panic; 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, 9790795Sgshapiro RF_COPYPARSE|RF_SENDERADDR, 9890795Sgshapiro '\0', NULL, e, false) == NULL) 9938032Speter { 10064565Sgshapiro syserr("553 5.3.5 Cannot parse Postmaster!"); 10190795Sgshapiro finis(true, true, EX_SOFTWARE); 10238032Speter } 10338032Speter } 10438032Speter e->e_to = NULL; 10538032Speter 10638032Speter /* 10738032Speter ** Basic state machine. 10838032Speter ** 10938032Speter ** This machine runs through the following states: 11038032Speter ** 11138032Speter ** ESM_QUIET Errors have already been printed iff the 11238032Speter ** sender is local. 11338032Speter ** ESM_REPORT Report directly to the sender's terminal. 11438032Speter ** ESM_MAIL Mail response to the sender. 11538032Speter ** ESM_DEADLETTER Save response in ~/dead.letter. 11638032Speter ** ESM_POSTMASTER Mail response to the postmaster. 11764565Sgshapiro ** ESM_DEADLETTERDROP 11864565Sgshapiro ** If DeadLetterDrop set, save it there. 11938032Speter ** ESM_PANIC Save response anywhere possible. 12038032Speter */ 12138032Speter 12238032Speter /* determine starting state */ 12338032Speter switch (e->e_errormode) 12438032Speter { 12538032Speter case EM_WRITE: 12638032Speter state = ESM_REPORT; 12738032Speter break; 12838032Speter 12938032Speter case EM_BERKNET: 13038032Speter case EM_MAIL: 13138032Speter state = ESM_MAIL; 13238032Speter break; 13338032Speter 13438032Speter case EM_PRINT: 13538032Speter case '\0': 13638032Speter state = ESM_QUIET; 13738032Speter break; 13838032Speter 13938032Speter case EM_QUIET: 14038032Speter /* no need to return anything at all */ 14190795Sgshapiro return panic; 14238032Speter 14338032Speter default: 14490795Sgshapiro syserr("554 5.3.0 savemail: bogus errormode x%x", 14564565Sgshapiro e->e_errormode); 14638032Speter state = ESM_MAIL; 14738032Speter break; 14838032Speter } 14938032Speter 15038032Speter /* if this is already an error response, send to postmaster */ 15138032Speter if (bitset(EF_RESPONSE, e->e_flags)) 15238032Speter { 15338032Speter if (e->e_parent != NULL && 15438032Speter bitset(EF_RESPONSE, e->e_parent->e_flags)) 15538032Speter { 15638032Speter /* got an error sending a response -- can it */ 15790795Sgshapiro return panic; 15838032Speter } 15938032Speter state = ESM_POSTMASTER; 16038032Speter } 16138032Speter 16238032Speter while (state != ESM_DONE) 16338032Speter { 16438032Speter if (tTd(6, 5)) 16590795Sgshapiro sm_dprintf(" state %d\n", state); 16638032Speter 16738032Speter switch (state) 16838032Speter { 16938032Speter case ESM_QUIET: 17038032Speter if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags)) 17138032Speter state = ESM_DEADLETTER; 17238032Speter else 17338032Speter state = ESM_MAIL; 17438032Speter break; 17538032Speter 17638032Speter case ESM_REPORT: 17738032Speter 17838032Speter /* 17938032Speter ** If the user is still logged in on the same terminal, 18038032Speter ** then write the error messages back to hir (sic). 18138032Speter */ 18238032Speter 183132946Sgshapiro#if USE_TTYPATH 18438032Speter p = ttypath(); 185132946Sgshapiro#else /* USE_TTYPATH */ 186132946Sgshapiro p = NULL; 187132946Sgshapiro#endif /* USE_TTYPATH */ 188132946Sgshapiro 18990795Sgshapiro if (p == NULL || sm_io_reopen(SmFtStdio, 19090795Sgshapiro SM_TIME_DEFAULT, 19190795Sgshapiro p, SM_IO_WRONLY, NULL, 19290795Sgshapiro smioout) == NULL) 19338032Speter { 19438032Speter state = ESM_MAIL; 19538032Speter break; 19638032Speter } 19738032Speter 19838032Speter expand("\201n", buf, sizeof buf, e); 19990795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 20090795Sgshapiro "\r\nMessage from %s...\r\n", buf); 20190795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 20290795Sgshapiro "Errors occurred while sending mail.\r\n"); 20338032Speter if (e->e_xfp != NULL) 20438032Speter { 20564565Sgshapiro (void) bfrewind(e->e_xfp); 20690795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 20790795Sgshapiro "Transcript follows:\r\n"); 20890795Sgshapiro while (sm_io_fgets(e->e_xfp, SM_TIME_DEFAULT, 20990795Sgshapiro buf, sizeof buf) != NULL && 21090795Sgshapiro !sm_io_error(smioout)) 21190795Sgshapiro (void) sm_io_fputs(smioout, 21290795Sgshapiro SM_TIME_DEFAULT, 21390795Sgshapiro buf); 21438032Speter } 21538032Speter else 21638032Speter { 21790795Sgshapiro syserr("Cannot open %s", 21890795Sgshapiro queuename(e, XSCRPT_LETTER)); 21990795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 22090795Sgshapiro "Transcript of session is unavailable.\r\n"); 22138032Speter } 22290795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 22390795Sgshapiro "Original message will be saved in dead.letter.\r\n"); 22438032Speter state = ESM_DEADLETTER; 22538032Speter break; 22638032Speter 22738032Speter case ESM_MAIL: 22838032Speter /* 22938032Speter ** If mailing back, do it. 23038032Speter ** Throw away all further output. Don't alias, 23138032Speter ** since this could cause loops, e.g., if joe 23238032Speter ** mails to joe@x, and for some reason the network 23338032Speter ** for @x is down, then the response gets sent to 23438032Speter ** joe@x, which gives a response, etc. Also force 23538032Speter ** the mail to be delivered even if a version of 23638032Speter ** it has already been sent to the sender. 23738032Speter ** 23838032Speter ** If this is a configuration or local software 23938032Speter ** error, send to the local postmaster as well, 24038032Speter ** since the originator can't do anything 24138032Speter ** about it anyway. Note that this is a full 24238032Speter ** copy of the message (intentionally) so that 24338032Speter ** the Postmaster can forward things along. 24438032Speter */ 24538032Speter 24638032Speter if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE) 24738032Speter { 24890795Sgshapiro (void) sendtolist("postmaster", NULLADDR, 24990795Sgshapiro &e->e_errorqueue, 0, e); 25038032Speter } 25138032Speter if (!emptyaddr(&e->e_from)) 25238032Speter { 25338032Speter char from[TOBUFSIZE]; 25438032Speter 25590795Sgshapiro if (sm_strlcpy(from, e->e_from.q_paddr, 25690795Sgshapiro sizeof from) >= sizeof from) 25738032Speter { 25838032Speter state = ESM_POSTMASTER; 25938032Speter break; 26038032Speter } 26138032Speter 26290795Sgshapiro if (!DontPruneRoutes) 26390795Sgshapiro (void) pruneroute(from); 26438032Speter 26538032Speter (void) sendtolist(from, NULLADDR, 26638032Speter &e->e_errorqueue, 0, e); 26738032Speter } 26838032Speter 26938032Speter /* 27038032Speter ** Deliver a non-delivery report to the 27138032Speter ** Postmaster-designate (not necessarily 27238032Speter ** Postmaster). This does not include the 27338032Speter ** body of the message, for privacy reasons. 27438032Speter ** You really shouldn't need this. 27538032Speter */ 27638032Speter 27738032Speter e->e_flags |= EF_PM_NOTIFY; 27838032Speter 27938032Speter /* check to see if there are any good addresses */ 28038032Speter for (q = e->e_errorqueue; q != NULL; q = q->q_next) 28164565Sgshapiro { 28264565Sgshapiro if (QS_IS_SENDABLE(q->q_state)) 28338032Speter break; 28464565Sgshapiro } 28538032Speter if (q == NULL) 28638032Speter { 28738032Speter /* this is an error-error */ 28838032Speter state = ESM_POSTMASTER; 28938032Speter break; 29038032Speter } 29138032Speter if (returntosender(e->e_message, e->e_errorqueue, 29238032Speter sendbody ? RTSF_SEND_BODY 29338032Speter : RTSF_NO_BODY, 29438032Speter e) == 0) 29538032Speter { 29638032Speter state = ESM_DONE; 29738032Speter break; 29838032Speter } 29938032Speter 30038032Speter /* didn't work -- return to postmaster */ 30138032Speter state = ESM_POSTMASTER; 30238032Speter break; 30338032Speter 30438032Speter case ESM_POSTMASTER: 30538032Speter /* 30638032Speter ** Similar to previous case, but to system postmaster. 30738032Speter */ 30838032Speter 30938032Speter q = NULL; 31064565Sgshapiro expand(DoubleBounceAddr, buf, sizeof buf, e); 31190795Sgshapiro 31290795Sgshapiro /* 31390795Sgshapiro ** Just drop it on the floor if DoubleBounceAddr 31490795Sgshapiro ** expands to an empty string. 31590795Sgshapiro */ 31690795Sgshapiro 31790795Sgshapiro if (*buf == '\0') 31890795Sgshapiro { 31990795Sgshapiro state = ESM_DONE; 32090795Sgshapiro break; 32190795Sgshapiro } 32264565Sgshapiro if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0) 32338032Speter { 32464565Sgshapiro syserr("553 5.3.0 cannot parse %s!", buf); 32538032Speter ExitStat = EX_SOFTWARE; 32664565Sgshapiro state = ESM_DEADLETTERDROP; 32738032Speter break; 32838032Speter } 32938032Speter flags = RTSF_PM_BOUNCE; 33038032Speter if (sendbody) 33138032Speter flags |= RTSF_SEND_BODY; 33238032Speter if (returntosender(e->e_message, q, flags, e) == 0) 33338032Speter { 33438032Speter state = ESM_DONE; 33538032Speter break; 33638032Speter } 33738032Speter 33838032Speter /* didn't work -- last resort */ 33964565Sgshapiro state = ESM_DEADLETTERDROP; 34038032Speter break; 34138032Speter 34238032Speter case ESM_DEADLETTER: 34338032Speter /* 34438032Speter ** Save the message in dead.letter. 34538032Speter ** If we weren't mailing back, and the user is 34638032Speter ** local, we should save the message in 34738032Speter ** ~/dead.letter so that the poor person doesn't 34838032Speter ** have to type it over again -- and we all know 34938032Speter ** what poor typists UNIX users are. 35038032Speter */ 35138032Speter 35238032Speter p = NULL; 35338032Speter if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags)) 35438032Speter { 35538032Speter if (e->e_from.q_home != NULL) 35638032Speter p = e->e_from.q_home; 35790795Sgshapiro else if (sm_mbdb_lookup(e->e_from.q_user, &user) 35890795Sgshapiro == EX_OK && 35990795Sgshapiro *user.mbdb_homedir != '\0') 36090795Sgshapiro p = user.mbdb_homedir; 36138032Speter } 36238032Speter if (p == NULL || e->e_dfp == NULL) 36338032Speter { 36438032Speter /* no local directory or no data file */ 36538032Speter state = ESM_MAIL; 36638032Speter break; 36738032Speter } 36838032Speter 36938032Speter /* we have a home directory; write dead.letter */ 37090795Sgshapiro macdefine(&e->e_macro, A_TEMP, 'z', p); 37164565Sgshapiro 37264565Sgshapiro /* get the sender for the UnixFromLine */ 37364565Sgshapiro p = macvalue('g', e); 37490795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); 37564565Sgshapiro 37698125Sgshapiro expand("\201z/dead.letter", dlbuf, sizeof dlbuf, e); 37764565Sgshapiro sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; 37838032Speter if (RealUid == 0) 37964565Sgshapiro sff |= SFF_ROOTOK; 38098125Sgshapiro e->e_to = dlbuf; 38198125Sgshapiro if (writable(dlbuf, NULL, sff) && 38298125Sgshapiro mailfile(dlbuf, FileMailer, NULL, sff, e) == EX_OK) 38338032Speter { 38438032Speter int oldverb = Verbose; 38538032Speter 38664565Sgshapiro if (OpMode != MD_DAEMON && OpMode != MD_SMTP) 38764565Sgshapiro Verbose = 1; 38864565Sgshapiro if (Verbose > 0) 38998125Sgshapiro message("Saved message in %s", dlbuf); 39038032Speter Verbose = oldverb; 39190795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', p); 39238032Speter state = ESM_DONE; 39338032Speter break; 39438032Speter } 39590795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', p); 39638032Speter state = ESM_MAIL; 39738032Speter break; 39838032Speter 39964565Sgshapiro case ESM_DEADLETTERDROP: 40038032Speter /* 40164565Sgshapiro ** Log the mail in DeadLetterDrop file. 40238032Speter */ 40338032Speter 40438032Speter if (e->e_class < 0) 40538032Speter { 40638032Speter state = ESM_DONE; 40738032Speter break; 40838032Speter } 40938032Speter 41038032Speter if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') || 41164565Sgshapiro DeadLetterDrop == NULL || 41264565Sgshapiro DeadLetterDrop[0] == '\0') 41338032Speter { 41438032Speter state = ESM_PANIC; 41538032Speter break; 41638032Speter } 41738032Speter 41864565Sgshapiro sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN; 41964565Sgshapiro if (!writable(DeadLetterDrop, NULL, sff) || 42038032Speter (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND, 42164565Sgshapiro FileMode, sff)) == NULL) 42238032Speter { 42338032Speter state = ESM_PANIC; 42438032Speter break; 42538032Speter } 42638032Speter 42764565Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 42838032Speter mcibuf.mci_out = fp; 42938032Speter mcibuf.mci_mailer = FileMailer; 43038032Speter if (bitnset(M_7BITS, FileMailer->m_flags)) 43138032Speter mcibuf.mci_flags |= MCIF_7BIT; 43238032Speter 43364565Sgshapiro /* get the sender for the UnixFromLine */ 43464565Sgshapiro p = macvalue('g', e); 43590795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); 43664565Sgshapiro 437157006Sgshapiro if (!putfromline(&mcibuf, e) || 438157006Sgshapiro !(*e->e_puthdr)(&mcibuf, e->e_header, e, 439157006Sgshapiro M87F_OUTER) || 440157006Sgshapiro !(*e->e_putbody)(&mcibuf, e, NULL) || 441157006Sgshapiro !putline("\n", &mcibuf) || 442157006Sgshapiro sm_io_flush(fp, SM_TIME_DEFAULT) == SM_IO_EOF || 443157006Sgshapiro sm_io_error(fp) || 44490795Sgshapiro sm_io_close(fp, SM_TIME_DEFAULT) < 0) 44538032Speter state = ESM_PANIC; 44638032Speter else 44738032Speter { 44838032Speter int oldverb = Verbose; 44938032Speter 45064565Sgshapiro if (OpMode != MD_DAEMON && OpMode != MD_SMTP) 45164565Sgshapiro Verbose = 1; 45264565Sgshapiro if (Verbose > 0) 45364565Sgshapiro message("Saved message in %s", 45464565Sgshapiro DeadLetterDrop); 45538032Speter Verbose = oldverb; 45638032Speter if (LogLevel > 3) 45738032Speter sm_syslog(LOG_NOTICE, e->e_id, 45864565Sgshapiro "Saved message in %s", 45964565Sgshapiro DeadLetterDrop); 46038032Speter state = ESM_DONE; 46138032Speter } 46290795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', p); 46338032Speter break; 46438032Speter 46538032Speter default: 46664565Sgshapiro syserr("554 5.3.5 savemail: unknown state %d", state); 46764565Sgshapiro /* FALLTHROUGH */ 46838032Speter 46938032Speter case ESM_PANIC: 47038032Speter /* leave the locked queue & transcript files around */ 47138032Speter loseqfile(e, "savemail panic"); 47290795Sgshapiro panic = true; 47366497Sgshapiro errno = 0; 47490795Sgshapiro syserr("554 savemail: cannot save rejected email anywhere"); 47590795Sgshapiro state = ESM_DONE; 47690795Sgshapiro break; 47738032Speter } 47838032Speter } 47990795Sgshapiro return panic; 48038032Speter} 48190795Sgshapiro/* 48238032Speter** RETURNTOSENDER -- return a message to the sender with an error. 48338032Speter** 48438032Speter** Parameters: 48538032Speter** msg -- the explanatory message. 48638032Speter** returnq -- the queue of people to send the message to. 48738032Speter** flags -- flags tweaking the operation: 48838032Speter** RTSF_SENDBODY -- include body of message (otherwise 48938032Speter** just send the header). 49038032Speter** RTSF_PMBOUNCE -- this is a postmaster bounce. 49138032Speter** e -- the current envelope. 49238032Speter** 49338032Speter** Returns: 49438032Speter** zero -- if everything went ok. 49538032Speter** else -- some error. 49638032Speter** 49738032Speter** Side Effects: 49890795Sgshapiro** Returns the current message to the sender via mail. 49938032Speter*/ 50038032Speter 50138032Speter#define MAXRETURNS 6 /* max depth of returning messages */ 50290795Sgshapiro#define ERRORFUDGE 1024 /* nominal size of error message text */ 50338032Speter 50438032Speterint 50538032Speterreturntosender(msg, returnq, flags, e) 50638032Speter char *msg; 50738032Speter ADDRESS *returnq; 50838032Speter int flags; 50938032Speter register ENVELOPE *e; 51038032Speter{ 51138032Speter register ENVELOPE *ee; 51238032Speter ENVELOPE *oldcur = CurEnv; 51338032Speter ENVELOPE errenvelope; 51438032Speter static int returndepth = 0; 51538032Speter register ADDRESS *q; 51638032Speter char *p; 51738032Speter char buf[MAXNAME + 1]; 51838032Speter 51938032Speter if (returnq == NULL) 52064565Sgshapiro return -1; 52138032Speter 52238032Speter if (msg == NULL) 52338032Speter msg = "Unable to deliver mail"; 52438032Speter 52538032Speter if (tTd(6, 1)) 52638032Speter { 52790795Sgshapiro sm_dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%p, returnq=", 52890795Sgshapiro msg, returndepth, e); 529132946Sgshapiro printaddr(sm_debug_file(), returnq, true); 53038032Speter if (tTd(6, 20)) 53138032Speter { 53290795Sgshapiro sm_dprintf("Sendq="); 533132946Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 53438032Speter } 53538032Speter } 53638032Speter 53738032Speter if (++returndepth >= MAXRETURNS) 53838032Speter { 53938032Speter if (returndepth != MAXRETURNS) 54064565Sgshapiro syserr("554 5.3.0 returntosender: infinite recursion on %s", 54164565Sgshapiro returnq->q_paddr); 54238032Speter /* don't "unrecurse" and fake a clean exit */ 54338032Speter /* returndepth--; */ 54464565Sgshapiro return 0; 54538032Speter } 54638032Speter 54790795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); 54890795Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', NULL); 54938032Speter 55038032Speter /* initialize error envelope */ 55190795Sgshapiro ee = newenvelope(&errenvelope, e, sm_rpool_new_x(NULL)); 55290795Sgshapiro macdefine(&ee->e_macro, A_PERM, 'a', "\201b"); 55390795Sgshapiro macdefine(&ee->e_macro, A_PERM, 'r', ""); 55490795Sgshapiro macdefine(&ee->e_macro, A_PERM, 's', "localhost"); 55590795Sgshapiro macdefine(&ee->e_macro, A_PERM, '_', "localhost"); 556110563Sgshapiro clrsessenvelope(ee); 55764565Sgshapiro 55838032Speter ee->e_puthdr = putheader; 55938032Speter ee->e_putbody = errbody; 56038032Speter ee->e_flags |= EF_RESPONSE|EF_METOO; 56138032Speter if (!bitset(EF_OLDSTYLE, e->e_flags)) 56238032Speter ee->e_flags &= ~EF_OLDSTYLE; 56364565Sgshapiro if (bitset(EF_DONT_MIME, e->e_flags)) 56464565Sgshapiro { 56564565Sgshapiro ee->e_flags |= EF_DONT_MIME; 56664565Sgshapiro 56764565Sgshapiro /* 56864565Sgshapiro ** If we can't convert to MIME and we don't pass 56964565Sgshapiro ** 8-bit, we can't send the body. 57064565Sgshapiro */ 57164565Sgshapiro 57264565Sgshapiro if (bitset(EF_HAS8BIT, e->e_flags) && 57364565Sgshapiro !bitset(MM_PASS8BIT, MimeMode)) 57464565Sgshapiro flags &= ~RTSF_SEND_BODY; 57564565Sgshapiro } 57664565Sgshapiro 57738032Speter ee->e_sendqueue = returnq; 57890795Sgshapiro ee->e_msgsize = 0; 57964565Sgshapiro if (bitset(RTSF_SEND_BODY, flags) && 58064565Sgshapiro !bitset(PRIV_NOBODYRETN, PrivacyFlags)) 58190795Sgshapiro ee->e_msgsize = ERRORFUDGE + e->e_msgsize; 58238032Speter else 58338032Speter ee->e_flags |= EF_NO_BODY_RETN; 58490795Sgshapiro 58590795Sgshapiro if (!setnewqueue(ee)) 58690795Sgshapiro { 58790795Sgshapiro syserr("554 5.3.0 returntosender: cannot select queue for %s", 58890795Sgshapiro returnq->q_paddr); 58990795Sgshapiro ExitStat = EX_UNAVAILABLE; 59090795Sgshapiro returndepth--; 59190795Sgshapiro return -1; 59290795Sgshapiro } 59338032Speter initsys(ee); 59464565Sgshapiro 59564565Sgshapiro#if NAMED_BIND 59664565Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 59764565Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 59864565Sgshapiro#endif /* NAMED_BIND */ 59938032Speter for (q = returnq; q != NULL; q = q->q_next) 60038032Speter { 60164565Sgshapiro if (QS_IS_BADADDR(q->q_state)) 60238032Speter continue; 60338032Speter 60438032Speter q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 60538032Speter q->q_flags |= QPINGONFAILURE; 60638032Speter 60764565Sgshapiro if (!QS_IS_DEAD(q->q_state)) 60838032Speter ee->e_nrcpts++; 60938032Speter 61038032Speter if (q->q_alias == NULL) 61190795Sgshapiro addheader("To", q->q_paddr, 0, ee); 61238032Speter } 61338032Speter 61438032Speter if (LogLevel > 5) 61538032Speter { 61664565Sgshapiro if (bitset(EF_RESPONSE, e->e_flags)) 61738032Speter p = "return to sender"; 61864565Sgshapiro else if (bitset(EF_WARNING, e->e_flags)) 61964565Sgshapiro p = "sender notify"; 62038032Speter else if (bitset(RTSF_PM_BOUNCE, flags)) 62138032Speter p = "postmaster notify"; 62238032Speter else 62338032Speter p = "DSN"; 62490795Sgshapiro sm_syslog(LOG_INFO, e->e_id, "%s: %s: %s", 62564565Sgshapiro ee->e_id, p, shortenstring(msg, MAXSHORTSTR)); 62638032Speter } 62738032Speter 62838032Speter if (SendMIMEErrors) 62938032Speter { 63090795Sgshapiro addheader("MIME-Version", "1.0", 0, ee); 63190795Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s.%ld/%.100s", 63290795Sgshapiro ee->e_id, (long)curtime(), MyHostName); 63390795Sgshapiro ee->e_msgboundary = sm_rpool_strdup_x(ee->e_rpool, buf); 63490795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 63538032Speter#if DSN 63664565Sgshapiro "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"", 63764565Sgshapiro#else /* DSN */ 63864565Sgshapiro "multipart/mixed; boundary=\"%s\"", 63964565Sgshapiro#endif /* DSN */ 64064565Sgshapiro ee->e_msgboundary); 64190795Sgshapiro addheader("Content-Type", buf, 0, ee); 64238032Speter 64338032Speter p = hvalue("Content-Transfer-Encoding", e->e_header); 64490795Sgshapiro if (p != NULL && sm_strcasecmp(p, "binary") != 0) 64538032Speter p = NULL; 64638032Speter if (p == NULL && bitset(EF_HAS8BIT, e->e_flags)) 64738032Speter p = "8bit"; 64838032Speter if (p != NULL) 64990795Sgshapiro addheader("Content-Transfer-Encoding", p, 0, ee); 65038032Speter } 65138032Speter if (strncmp(msg, "Warning:", 8) == 0) 65238032Speter { 65390795Sgshapiro addheader("Subject", msg, 0, ee); 65438032Speter p = "warning-timeout"; 65538032Speter } 65638032Speter else if (strncmp(msg, "Postmaster warning:", 19) == 0) 65738032Speter { 65890795Sgshapiro addheader("Subject", msg, 0, ee); 65938032Speter p = "postmaster-warning"; 66038032Speter } 66138032Speter else if (strcmp(msg, "Return receipt") == 0) 66238032Speter { 66390795Sgshapiro addheader("Subject", msg, 0, ee); 66438032Speter p = "return-receipt"; 66538032Speter } 66638032Speter else if (bitset(RTSF_PM_BOUNCE, flags)) 66738032Speter { 66890795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 66964565Sgshapiro "Postmaster notify: see transcript for details"); 67090795Sgshapiro addheader("Subject", buf, 0, ee); 67138032Speter p = "postmaster-notification"; 67238032Speter } 67338032Speter else 67438032Speter { 67590795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 67664565Sgshapiro "Returned mail: see transcript for details"); 67790795Sgshapiro addheader("Subject", buf, 0, ee); 67838032Speter p = "failure"; 67938032Speter } 68090795Sgshapiro (void) sm_snprintf(buf, sizeof buf, "auto-generated (%s)", p); 68190795Sgshapiro addheader("Auto-Submitted", buf, 0, ee); 68238032Speter 68338032Speter /* fake up an address header for the from person */ 68438032Speter expand("\201n", buf, sizeof buf, e); 68564565Sgshapiro if (parseaddr(buf, &ee->e_from, 68690795Sgshapiro RF_COPYALL|RF_SENDERADDR, '\0', NULL, e, false) == NULL) 68738032Speter { 68864565Sgshapiro syserr("553 5.3.5 Can't parse myself!"); 68938032Speter ExitStat = EX_SOFTWARE; 69038032Speter returndepth--; 69164565Sgshapiro return -1; 69238032Speter } 69338032Speter ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 69438032Speter ee->e_from.q_flags |= QPINGONFAILURE; 69538032Speter ee->e_sender = ee->e_from.q_paddr; 69638032Speter 69738032Speter /* push state into submessage */ 69838032Speter CurEnv = ee; 69990795Sgshapiro macdefine(&ee->e_macro, A_PERM, 'f', "\201n"); 70090795Sgshapiro macdefine(&ee->e_macro, A_PERM, 'x', "Mail Delivery Subsystem"); 70190795Sgshapiro eatheader(ee, true, true); 70238032Speter 70338032Speter /* mark statistics */ 70490795Sgshapiro markstats(ee, NULLADDR, STATS_NORMAL); 70538032Speter 70638032Speter /* actually deliver the error message */ 70738032Speter sendall(ee, SM_DELIVER); 70838032Speter 70938032Speter /* restore state */ 71090795Sgshapiro dropenvelope(ee, true, false); 71190795Sgshapiro sm_rpool_free(ee->e_rpool); 71238032Speter CurEnv = oldcur; 71338032Speter returndepth--; 71438032Speter 71538032Speter /* check for delivery errors */ 71664565Sgshapiro if (ee->e_parent == NULL || 71764565Sgshapiro !bitset(EF_RESPONSE, ee->e_parent->e_flags)) 71838032Speter return 0; 71938032Speter for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 72038032Speter { 72164565Sgshapiro if (QS_IS_ATTEMPTED(q->q_state)) 72238032Speter return 0; 72338032Speter } 72438032Speter return -1; 72538032Speter} 72690795Sgshapiro/* 72738032Speter** ERRBODY -- output the body of an error message. 72838032Speter** 72938032Speter** Typically this is a copy of the transcript plus a copy of the 73038032Speter** original offending message. 73138032Speter** 73238032Speter** Parameters: 73338032Speter** mci -- the mailer connection information. 73438032Speter** e -- the envelope we are working in. 73590795Sgshapiro** separator -- any possible MIME separator (unused). 73638032Speter** 73738032Speter** Returns: 738159613Sgshapiro** true iff body was written successfully 73938032Speter** 74038032Speter** Side Effects: 74138032Speter** Outputs the body of an error message. 74238032Speter*/ 74338032Speter 74464565Sgshapiro/* ARGSUSED2 */ 745157006Sgshapirostatic bool 74638032Spetererrbody(mci, e, separator) 74738032Speter register MCI *mci; 74838032Speter register ENVELOPE *e; 74938032Speter char *separator; 75038032Speter{ 75164565Sgshapiro bool printheader; 75264565Sgshapiro bool sendbody; 75364565Sgshapiro bool pm_notify; 75464565Sgshapiro int save_errno; 75590795Sgshapiro register SM_FILE_T *xfile; 75638032Speter char *p; 75738032Speter register ADDRESS *q = NULL; 75890795Sgshapiro char actual[MAXLINE]; 75938032Speter char buf[MAXLINE]; 76038032Speter 76138032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 76238032Speter { 763157006Sgshapiro if (!putline("", mci)) 764157006Sgshapiro goto writeerr; 76538032Speter mci->mci_flags &= ~MCIF_INHEADER; 76638032Speter } 76738032Speter if (e->e_parent == NULL) 76838032Speter { 76938032Speter syserr("errbody: null parent"); 770157006Sgshapiro if (!putline(" ----- Original message lost -----\n", mci)) 771157006Sgshapiro goto writeerr; 772157006Sgshapiro return true; 77338032Speter } 77438032Speter 77538032Speter /* 77638032Speter ** Output MIME header. 77738032Speter */ 77838032Speter 77938032Speter if (e->e_msgboundary != NULL) 78038032Speter { 78190795Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary); 782157006Sgshapiro if (!putline("This is a MIME-encapsulated message", mci) || 783157006Sgshapiro !putline("", mci) || 784157006Sgshapiro !putline(buf, mci) || 785157006Sgshapiro !putline("", mci)) 786157006Sgshapiro goto writeerr; 78738032Speter } 78838032Speter 78938032Speter /* 79038032Speter ** Output introductory information. 79138032Speter */ 79238032Speter 79390795Sgshapiro pm_notify = false; 79438032Speter p = hvalue("subject", e->e_header); 79538032Speter if (p != NULL && strncmp(p, "Postmaster ", 11) == 0) 79690795Sgshapiro pm_notify = true; 79738032Speter else 79838032Speter { 79938032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 80064565Sgshapiro { 80164565Sgshapiro if (QS_IS_BADADDR(q->q_state)) 80238032Speter break; 80364565Sgshapiro } 80438032Speter } 80538032Speter if (!pm_notify && q == NULL && 80638032Speter !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags)) 80738032Speter { 808157006Sgshapiro if (!putline(" **********************************************", 809157006Sgshapiro mci) || 810157006Sgshapiro !putline(" ** THIS IS A WARNING MESSAGE ONLY **", 811157006Sgshapiro mci) || 812157006Sgshapiro !putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **", 813157006Sgshapiro mci) || 814157006Sgshapiro !putline(" **********************************************", 815157006Sgshapiro mci) || 816157006Sgshapiro !putline("", mci)) 817157006Sgshapiro goto writeerr; 81838032Speter } 81990795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 82090795Sgshapiro "The original message was received at %s", 82190795Sgshapiro arpadate(ctime(&e->e_parent->e_ctime))); 822157006Sgshapiro if (!putline(buf, mci)) 823157006Sgshapiro goto writeerr; 82438032Speter expand("from \201_", buf, sizeof buf, e->e_parent); 825157006Sgshapiro if (!putline(buf, mci)) 826157006Sgshapiro goto writeerr; 82764565Sgshapiro 82864565Sgshapiro /* include id in postmaster copies */ 82964565Sgshapiro if (pm_notify && e->e_parent->e_id != NULL) 83064565Sgshapiro { 83190795Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, "with id ", 83290795Sgshapiro e->e_parent->e_id); 833157006Sgshapiro if (!putline(buf, mci)) 834157006Sgshapiro goto writeerr; 83564565Sgshapiro } 836157006Sgshapiro if (!putline("", mci)) 837157006Sgshapiro goto writeerr; 83838032Speter 83938032Speter /* 84038032Speter ** Output error message header (if specified and available). 84138032Speter */ 84238032Speter 84364565Sgshapiro if (ErrMsgFile != NULL && 84464565Sgshapiro !bitset(EF_SENDRECEIPT, e->e_parent->e_flags)) 84538032Speter { 84638032Speter if (*ErrMsgFile == '/') 84738032Speter { 84864565Sgshapiro long sff = SFF_ROOTOK|SFF_REGONLY; 84938032Speter 85038032Speter if (DontLockReadFiles) 85138032Speter sff |= SFF_NOLOCK; 85264565Sgshapiro if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH, 85364565Sgshapiro DontBlameSendmail)) 85438032Speter sff |= SFF_SAFEDIRPATH; 85538032Speter xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff); 85638032Speter if (xfile != NULL) 85738032Speter { 85890795Sgshapiro while (sm_io_fgets(xfile, SM_TIME_DEFAULT, buf, 85990795Sgshapiro sizeof buf) != NULL) 86038032Speter { 86138032Speter translate_dollars(buf); 86238032Speter expand(buf, buf, sizeof buf, e); 863157006Sgshapiro if (!putline(buf, mci)) 864157006Sgshapiro goto writeerr; 86538032Speter } 86690795Sgshapiro (void) sm_io_close(xfile, SM_TIME_DEFAULT); 867157006Sgshapiro if (!putline("\n", mci)) 868157006Sgshapiro goto writeerr; 86938032Speter } 87038032Speter } 87138032Speter else 87238032Speter { 87338032Speter expand(ErrMsgFile, buf, sizeof buf, e); 874157006Sgshapiro if (!putline(buf, mci) || !putline("", mci)) 875157006Sgshapiro goto writeerr; 87638032Speter } 87738032Speter } 87838032Speter 87938032Speter /* 88038032Speter ** Output message introduction 88138032Speter */ 88238032Speter 88390795Sgshapiro /* permanent fatal errors */ 88490795Sgshapiro printheader = true; 88538032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 88638032Speter { 88764565Sgshapiro if (!QS_IS_BADADDR(q->q_state) || 88838032Speter !bitset(QPINGONFAILURE, q->q_flags)) 88938032Speter continue; 89038032Speter 89138032Speter if (printheader) 89238032Speter { 893157006Sgshapiro if (!putline(" ----- The following addresses had permanent fatal errors -----", 894157006Sgshapiro mci)) 895157006Sgshapiro goto writeerr; 89690795Sgshapiro printheader = false; 89738032Speter } 89838032Speter 89990795Sgshapiro (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR), 90090795Sgshapiro sizeof buf); 901157006Sgshapiro if (!putline(buf, mci)) 902157006Sgshapiro goto writeerr; 90364565Sgshapiro if (q->q_rstatus != NULL) 90464565Sgshapiro { 90590795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 90690795Sgshapiro " (reason: %s)", 90790795Sgshapiro shortenstring(exitstat(q->q_rstatus), 90890795Sgshapiro MAXSHORTSTR)); 909157006Sgshapiro if (!putline(buf, mci)) 910157006Sgshapiro goto writeerr; 91164565Sgshapiro } 91238032Speter if (q->q_alias != NULL) 91338032Speter { 91490795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 91590795Sgshapiro " (expanded from: %s)", 91690795Sgshapiro shortenstring(q->q_alias->q_paddr, 91790795Sgshapiro MAXSHORTSTR)); 918157006Sgshapiro if (!putline(buf, mci)) 919157006Sgshapiro goto writeerr; 92038032Speter } 92138032Speter } 922157006Sgshapiro if (!printheader && !putline("", mci)) 923157006Sgshapiro goto writeerr; 92438032Speter 92590795Sgshapiro /* transient non-fatal errors */ 92690795Sgshapiro printheader = true; 92738032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 92838032Speter { 92964565Sgshapiro if (QS_IS_BADADDR(q->q_state) || 93038032Speter !bitset(QPRIMARY, q->q_flags) || 93190795Sgshapiro !bitset(QBYNDELAY, q->q_flags) || 93238032Speter !bitset(QDELAYED, q->q_flags)) 93338032Speter continue; 93438032Speter 93538032Speter if (printheader) 93638032Speter { 937157006Sgshapiro if (!putline(" ----- The following addresses had transient non-fatal errors -----", 938157006Sgshapiro mci)) 939157006Sgshapiro goto writeerr; 94090795Sgshapiro printheader = false; 94138032Speter } 94238032Speter 94390795Sgshapiro (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR), 94490795Sgshapiro sizeof buf); 945157006Sgshapiro if (!putline(buf, mci)) 946157006Sgshapiro goto writeerr; 94738032Speter if (q->q_alias != NULL) 94838032Speter { 94990795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 95090795Sgshapiro " (expanded from: %s)", 95190795Sgshapiro shortenstring(q->q_alias->q_paddr, 95290795Sgshapiro MAXSHORTSTR)); 953157006Sgshapiro if (!putline(buf, mci)) 954157006Sgshapiro goto writeerr; 95538032Speter } 95638032Speter } 957157006Sgshapiro if (!printheader && !putline("", mci)) 958157006Sgshapiro goto writeerr; 95938032Speter 96090795Sgshapiro /* successful delivery notifications */ 96190795Sgshapiro printheader = true; 96238032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 96338032Speter { 96464565Sgshapiro if (QS_IS_BADADDR(q->q_state) || 96538032Speter !bitset(QPRIMARY, q->q_flags) || 96690795Sgshapiro bitset(QBYNDELAY, q->q_flags) || 96738032Speter bitset(QDELAYED, q->q_flags)) 96838032Speter continue; 96990795Sgshapiro else if (bitset(QBYNRELAY, q->q_flags)) 97090795Sgshapiro p = "Deliver-By notify: relayed"; 97190795Sgshapiro else if (bitset(QBYTRACE, q->q_flags)) 97290795Sgshapiro p = "Deliver-By trace: relayed"; 97338032Speter else if (!bitset(QPINGONSUCCESS, q->q_flags)) 97438032Speter continue; 97538032Speter else if (bitset(QRELAYED, q->q_flags)) 97638032Speter p = "relayed to non-DSN-aware mailer"; 97738032Speter else if (bitset(QDELIVERED, q->q_flags)) 97838032Speter { 97938032Speter if (bitset(QEXPANDED, q->q_flags)) 98038032Speter p = "successfully delivered to mailing list"; 98138032Speter else 98238032Speter p = "successfully delivered to mailbox"; 98338032Speter } 98438032Speter else if (bitset(QEXPANDED, q->q_flags)) 98538032Speter p = "expanded by alias"; 98638032Speter else 98738032Speter continue; 98838032Speter 98938032Speter if (printheader) 99038032Speter { 991157006Sgshapiro if (!putline(" ----- The following addresses had successful delivery notifications -----", 992157006Sgshapiro mci)) 993157006Sgshapiro goto writeerr; 99490795Sgshapiro printheader = false; 99538032Speter } 99638032Speter 99790795Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s (%s)", 99864565Sgshapiro shortenstring(q->q_paddr, MAXSHORTSTR), p); 999157006Sgshapiro if (!putline(buf, mci)) 1000157006Sgshapiro goto writeerr; 100138032Speter if (q->q_alias != NULL) 100238032Speter { 100390795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 100490795Sgshapiro " (expanded from: %s)", 100590795Sgshapiro shortenstring(q->q_alias->q_paddr, 100690795Sgshapiro MAXSHORTSTR)); 1007157006Sgshapiro if (!putline(buf, mci)) 1008157006Sgshapiro goto writeerr; 100938032Speter } 101038032Speter } 1011157006Sgshapiro if (!printheader && !putline("", mci)) 1012157006Sgshapiro goto writeerr; 101338032Speter 101438032Speter /* 101538032Speter ** Output transcript of errors 101638032Speter */ 101738032Speter 101890795Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 101964565Sgshapiro if (e->e_parent->e_xfp == NULL) 102038032Speter { 1021157006Sgshapiro if (!putline(" ----- Transcript of session is unavailable -----\n", 1022157006Sgshapiro mci)) 1023157006Sgshapiro goto writeerr; 102438032Speter } 102538032Speter else 102638032Speter { 102790795Sgshapiro printheader = true; 102864565Sgshapiro (void) bfrewind(e->e_parent->e_xfp); 102938032Speter if (e->e_xfp != NULL) 103090795Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 103190795Sgshapiro while (sm_io_fgets(e->e_parent->e_xfp, SM_TIME_DEFAULT, buf, 103290795Sgshapiro sizeof buf) != NULL) 103338032Speter { 1034157006Sgshapiro if (printheader && !putline(" ----- Transcript of session follows -----\n", 1035157006Sgshapiro mci)) 1036157006Sgshapiro goto writeerr; 103790795Sgshapiro printheader = false; 1038157006Sgshapiro if (!putline(buf, mci)) 1039157006Sgshapiro goto writeerr; 104038032Speter } 104138032Speter } 104238032Speter errno = 0; 104338032Speter 104438032Speter#if DSN 104538032Speter /* 104638032Speter ** Output machine-readable version. 104738032Speter */ 104838032Speter 104938032Speter if (e->e_msgboundary != NULL) 105038032Speter { 105190795Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary); 1052157006Sgshapiro if (!putline("", mci) || 1053157006Sgshapiro !putline(buf, mci) || 1054157006Sgshapiro !putline("Content-Type: message/delivery-status", mci) || 1055157006Sgshapiro !putline("", mci)) 1056157006Sgshapiro goto writeerr; 105738032Speter 105838032Speter /* 105938032Speter ** Output per-message information. 106038032Speter */ 106138032Speter 106238032Speter /* original envelope id from MAIL FROM: line */ 106338032Speter if (e->e_parent->e_envid != NULL) 106438032Speter { 106590795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 106664565Sgshapiro "Original-Envelope-Id: %.800s", 106764565Sgshapiro xuntextify(e->e_parent->e_envid)); 1068157006Sgshapiro if (!putline(buf, mci)) 1069157006Sgshapiro goto writeerr; 107038032Speter } 107138032Speter 107238032Speter /* Reporting-MTA: is us (required) */ 107390795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 107490795Sgshapiro "Reporting-MTA: dns; %.800s", MyHostName); 1075157006Sgshapiro if (!putline(buf, mci)) 1076157006Sgshapiro goto writeerr; 107738032Speter 107838032Speter /* DSN-Gateway: not relevant since we are not translating */ 107938032Speter 108038032Speter /* Received-From-MTA: shows where we got this message from */ 108138032Speter if (RealHostName != NULL) 108238032Speter { 108338032Speter /* XXX use $s for type? */ 108438032Speter if (e->e_parent->e_from.q_mailer == NULL || 108538032Speter (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL) 108638032Speter p = "dns"; 108790795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 108864565Sgshapiro "Received-From-MTA: %s; %.800s", 108964565Sgshapiro p, RealHostName); 1090157006Sgshapiro if (!putline(buf, mci)) 1091157006Sgshapiro goto writeerr; 109238032Speter } 109338032Speter 109438032Speter /* Arrival-Date: -- when it arrived here */ 109590795Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, "Arrival-Date: ", 109664565Sgshapiro arpadate(ctime(&e->e_parent->e_ctime))); 1097157006Sgshapiro if (!putline(buf, mci)) 1098157006Sgshapiro goto writeerr; 109938032Speter 110090795Sgshapiro /* Deliver-By-Date: -- when it should have been delivered */ 110190795Sgshapiro if (IS_DLVR_BY(e->e_parent)) 110290795Sgshapiro { 110390795Sgshapiro time_t dbyd; 110490795Sgshapiro 110590795Sgshapiro dbyd = e->e_parent->e_ctime + e->e_parent->e_deliver_by; 110690795Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, 110790795Sgshapiro "Deliver-By-Date: ", 110890795Sgshapiro arpadate(ctime(&dbyd))); 1109157006Sgshapiro if (!putline(buf, mci)) 1110157006Sgshapiro goto writeerr; 111190795Sgshapiro } 111290795Sgshapiro 111338032Speter /* 111438032Speter ** Output per-address information. 111538032Speter */ 111638032Speter 111738032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 111838032Speter { 111938032Speter char *action; 112038032Speter 112164565Sgshapiro if (QS_IS_BADADDR(q->q_state)) 112271348Sgshapiro { 112371348Sgshapiro /* RFC 1891, 6.2.6 (b) */ 112471348Sgshapiro if (bitset(QHASNOTIFY, q->q_flags) && 112571348Sgshapiro !bitset(QPINGONFAILURE, q->q_flags)) 112671348Sgshapiro continue; 112738032Speter action = "failed"; 112871348Sgshapiro } 112938032Speter else if (!bitset(QPRIMARY, q->q_flags)) 113038032Speter continue; 113138032Speter else if (bitset(QDELIVERED, q->q_flags)) 113238032Speter { 113338032Speter if (bitset(QEXPANDED, q->q_flags)) 113438032Speter action = "delivered (to mailing list)"; 113538032Speter else 113638032Speter action = "delivered (to mailbox)"; 113738032Speter } 113838032Speter else if (bitset(QRELAYED, q->q_flags)) 113938032Speter action = "relayed (to non-DSN-aware mailer)"; 114038032Speter else if (bitset(QEXPANDED, q->q_flags)) 114138032Speter action = "expanded (to multi-recipient alias)"; 114238032Speter else if (bitset(QDELAYED, q->q_flags)) 114338032Speter action = "delayed"; 114490795Sgshapiro else if (bitset(QBYTRACE, q->q_flags)) 114590795Sgshapiro action = "relayed (Deliver-By trace mode)"; 114690795Sgshapiro else if (bitset(QBYNDELAY, q->q_flags)) 114790795Sgshapiro action = "delayed (Deliver-By notify mode)"; 114890795Sgshapiro else if (bitset(QBYNRELAY, q->q_flags)) 114990795Sgshapiro action = "relayed (Deliver-By notify mode)"; 115038032Speter else 115138032Speter continue; 115238032Speter 1153157006Sgshapiro if (!putline("", mci)) 1154157006Sgshapiro goto writeerr; 115538032Speter 115638032Speter /* Original-Recipient: -- passed from on high */ 115738032Speter if (q->q_orcpt != NULL) 115838032Speter { 115990795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 116064565Sgshapiro "Original-Recipient: %.800s", 116164565Sgshapiro q->q_orcpt); 1162157006Sgshapiro if (!putline(buf, mci)) 1163157006Sgshapiro goto writeerr; 116438032Speter } 116538032Speter 116690795Sgshapiro /* Figure out actual recipient */ 116790795Sgshapiro actual[0] = '\0'; 116890795Sgshapiro if (q->q_user[0] != '\0') 116938032Speter { 117064565Sgshapiro if (q->q_mailer != NULL && 117164565Sgshapiro q->q_mailer->m_addrtype != NULL) 117264565Sgshapiro p = q->q_mailer->m_addrtype; 117364565Sgshapiro else 117464565Sgshapiro p = "rfc822"; 117564565Sgshapiro 117690795Sgshapiro if (sm_strcasecmp(p, "rfc822") == 0 && 117764565Sgshapiro strchr(q->q_user, '@') == NULL) 117838032Speter { 117990795Sgshapiro (void) sm_snprintf(actual, 118090795Sgshapiro sizeof actual, 118190795Sgshapiro "%s; %.700s@%.100s", 118290795Sgshapiro p, q->q_user, 118390795Sgshapiro MyHostName); 118438032Speter } 118538032Speter else 118638032Speter { 118790795Sgshapiro (void) sm_snprintf(actual, 118890795Sgshapiro sizeof actual, 118990795Sgshapiro "%s; %.800s", 119090795Sgshapiro p, q->q_user); 119138032Speter } 119290795Sgshapiro } 119390795Sgshapiro 119490795Sgshapiro /* Final-Recipient: -- the name from the RCPT command */ 119590795Sgshapiro if (q->q_finalrcpt == NULL) 119690795Sgshapiro { 119790795Sgshapiro /* should never happen */ 119890795Sgshapiro sm_syslog(LOG_ERR, e->e_id, 119990795Sgshapiro "returntosender: q_finalrcpt is NULL"); 120090795Sgshapiro 120190795Sgshapiro /* try to fall back to the actual recipient */ 120290795Sgshapiro if (actual[0] != '\0') 120390795Sgshapiro q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool, 120490795Sgshapiro actual); 120590795Sgshapiro } 120690795Sgshapiro 120790795Sgshapiro if (q->q_finalrcpt != NULL) 120890795Sgshapiro { 120990795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 121090795Sgshapiro "Final-Recipient: %s", 121190795Sgshapiro q->q_finalrcpt); 1212157006Sgshapiro if (!putline(buf, mci)) 1213157006Sgshapiro goto writeerr; 121438032Speter } 121538032Speter 121690795Sgshapiro /* X-Actual-Recipient: -- the real problem address */ 121790795Sgshapiro if (actual[0] != '\0' && 121890795Sgshapiro q->q_finalrcpt != NULL && 1219141862Sgshapiro#if _FFR_PRIV_NOACTUALRECIPIENT 1220141862Sgshapiro !bitset(PRIV_NOACTUALRECIPIENT, PrivacyFlags) && 1221141862Sgshapiro#endif /* _FFR_PRIV_NOACTUALRECIPIENT */ 122290795Sgshapiro strcmp(actual, q->q_finalrcpt) != 0) 122390795Sgshapiro { 122490795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 122590795Sgshapiro "X-Actual-Recipient: %s", 122690795Sgshapiro actual); 1227157006Sgshapiro if (!putline(buf, mci)) 1228157006Sgshapiro goto writeerr; 122990795Sgshapiro } 123090795Sgshapiro 123138032Speter /* Action: -- what happened? */ 123290795Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, "Action: ", 123390795Sgshapiro action); 1234157006Sgshapiro if (!putline(buf, mci)) 1235157006Sgshapiro goto writeerr; 123638032Speter 123738032Speter /* Status: -- what _really_ happened? */ 123838032Speter if (q->q_status != NULL) 123938032Speter p = q->q_status; 124064565Sgshapiro else if (QS_IS_BADADDR(q->q_state)) 124138032Speter p = "5.0.0"; 124264565Sgshapiro else if (QS_IS_QUEUEUP(q->q_state)) 124338032Speter p = "4.0.0"; 124438032Speter else 124538032Speter p = "2.0.0"; 124690795Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, "Status: ", p); 1247157006Sgshapiro if (!putline(buf, mci)) 1248157006Sgshapiro goto writeerr; 124938032Speter 125038032Speter /* Remote-MTA: -- who was I talking to? */ 125138032Speter if (q->q_statmta != NULL) 125238032Speter { 125338032Speter if (q->q_mailer == NULL || 125438032Speter (p = q->q_mailer->m_mtatype) == NULL) 125538032Speter p = "dns"; 125690795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 125764565Sgshapiro "Remote-MTA: %s; %.800s", 125864565Sgshapiro p, q->q_statmta); 125938032Speter p = &buf[strlen(buf) - 1]; 126038032Speter if (*p == '.') 126138032Speter *p = '\0'; 1262157006Sgshapiro if (!putline(buf, mci)) 1263157006Sgshapiro goto writeerr; 126438032Speter } 126538032Speter 126638032Speter /* Diagnostic-Code: -- actual result from other end */ 126738032Speter if (q->q_rstatus != NULL) 126838032Speter { 1269159613Sgshapiro if (q->q_mailer == NULL || 1270159613Sgshapiro (p = q->q_mailer->m_diagtype) == NULL) 127138032Speter p = "smtp"; 127290795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 127364565Sgshapiro "Diagnostic-Code: %s; %.800s", 127464565Sgshapiro p, q->q_rstatus); 1275157006Sgshapiro if (!putline(buf, mci)) 1276157006Sgshapiro goto writeerr; 127738032Speter } 127838032Speter 127938032Speter /* Last-Attempt-Date: -- fine granularity */ 128038032Speter if (q->q_statdate == (time_t) 0L) 128190795Sgshapiro q->q_statdate = curtime(); 128290795Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, 128390795Sgshapiro "Last-Attempt-Date: ", 128464565Sgshapiro arpadate(ctime(&q->q_statdate))); 1285157006Sgshapiro if (!putline(buf, mci)) 1286157006Sgshapiro goto writeerr; 128738032Speter 128838032Speter /* Will-Retry-Until: -- for delayed messages only */ 128964565Sgshapiro if (QS_IS_QUEUEUP(q->q_state)) 129038032Speter { 129138032Speter time_t xdate; 129238032Speter 129338032Speter xdate = e->e_parent->e_ctime + 129438032Speter TimeOuts.to_q_return[e->e_parent->e_timeoutclass]; 129590795Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, 129690795Sgshapiro "Will-Retry-Until: ", 129764565Sgshapiro arpadate(ctime(&xdate))); 1298157006Sgshapiro if (!putline(buf, mci)) 1299157006Sgshapiro goto writeerr; 130038032Speter } 130138032Speter } 130238032Speter } 130364565Sgshapiro#endif /* DSN */ 130438032Speter 130538032Speter /* 130638032Speter ** Output text of original message 130738032Speter */ 130838032Speter 1309157006Sgshapiro if (!putline("", mci)) 1310157006Sgshapiro goto writeerr; 131138032Speter if (bitset(EF_HAS_DF, e->e_parent->e_flags)) 131238032Speter { 131338032Speter sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) && 131438032Speter !bitset(EF_NO_BODY_RETN, e->e_flags); 131538032Speter 131638032Speter if (e->e_msgboundary == NULL) 131738032Speter { 1318157006Sgshapiro if (!putline( 1319157006Sgshapiro sendbody 1320157006Sgshapiro ? " ----- Original message follows -----\n" 1321157006Sgshapiro : " ----- Message header follows -----\n", 1322157006Sgshapiro mci)) 1323157006Sgshapiro { 1324157006Sgshapiro goto writeerr; 1325157006Sgshapiro } 132638032Speter } 132738032Speter else 132838032Speter { 132990795Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, "--", 133064565Sgshapiro e->e_msgboundary); 133138032Speter 1332157006Sgshapiro if (!putline(buf, mci)) 1333157006Sgshapiro goto writeerr; 133490795Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, "Content-Type: ", 133564565Sgshapiro sendbody ? "message/rfc822" 133664565Sgshapiro : "text/rfc822-headers"); 1337157006Sgshapiro if (!putline(buf, mci)) 1338157006Sgshapiro goto writeerr; 133938032Speter 134064565Sgshapiro p = hvalue("Content-Transfer-Encoding", 134164565Sgshapiro e->e_parent->e_header); 134290795Sgshapiro if (p != NULL && sm_strcasecmp(p, "binary") != 0) 134338032Speter p = NULL; 134464565Sgshapiro if (p == NULL && 134564565Sgshapiro bitset(EF_HAS8BIT, e->e_parent->e_flags)) 134638032Speter p = "8bit"; 134738032Speter if (p != NULL) 134838032Speter { 134990795Sgshapiro (void) sm_snprintf(buf, sizeof buf, 135064565Sgshapiro "Content-Transfer-Encoding: %s", 135164565Sgshapiro p); 1352157006Sgshapiro if (!putline(buf, mci)) 1353157006Sgshapiro goto writeerr; 135438032Speter } 135538032Speter } 1356157006Sgshapiro if (!putline("", mci)) 1357157006Sgshapiro goto writeerr; 135864565Sgshapiro save_errno = errno; 1359157006Sgshapiro if (!putheader(mci, e->e_parent->e_header, e->e_parent, 1360157006Sgshapiro M87F_OUTER)) 1361157006Sgshapiro goto writeerr; 136264565Sgshapiro errno = save_errno; 136338032Speter if (sendbody) 1364157006Sgshapiro { 1365157006Sgshapiro if (!putbody(mci, e->e_parent, e->e_msgboundary)) 1366157006Sgshapiro goto writeerr; 1367157006Sgshapiro } 136838032Speter else if (e->e_msgboundary == NULL) 136938032Speter { 1370157006Sgshapiro if (!putline("", mci) || 1371157006Sgshapiro !putline(" ----- Message body suppressed -----", 1372157006Sgshapiro mci)) 1373157006Sgshapiro { 1374157006Sgshapiro goto writeerr; 1375157006Sgshapiro } 137638032Speter } 137738032Speter } 137838032Speter else if (e->e_msgboundary == NULL) 137938032Speter { 1380157006Sgshapiro if (!putline(" ----- No message was collected -----\n", mci)) 1381157006Sgshapiro goto writeerr; 138238032Speter } 138338032Speter 138438032Speter if (e->e_msgboundary != NULL) 138538032Speter { 138690795Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 3, "--", e->e_msgboundary, 138790795Sgshapiro "--"); 1388157006Sgshapiro if (!putline("", mci) || !putline(buf, mci)) 1389157006Sgshapiro goto writeerr; 139038032Speter } 1391157006Sgshapiro if (!putline("", mci) || 1392157006Sgshapiro sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF) 1393157006Sgshapiro goto writeerr; 139438032Speter 139538032Speter /* 139638032Speter ** Cleanup and exit 139738032Speter */ 139838032Speter 139938032Speter if (errno != 0) 1400157006Sgshapiro { 1401157006Sgshapiro writeerr: 140238032Speter syserr("errbody: I/O error"); 1403157006Sgshapiro return false; 1404157006Sgshapiro } 1405157006Sgshapiro return true; 140638032Speter} 1407157006Sgshapiro 140890795Sgshapiro/* 140938032Speter** SMTPTODSN -- convert SMTP to DSN status code 141038032Speter** 141138032Speter** Parameters: 141238032Speter** smtpstat -- the smtp status code (e.g., 550). 141338032Speter** 141438032Speter** Returns: 141538032Speter** The DSN version of the status code. 141690795Sgshapiro** 141790795Sgshapiro** Storage Management: 141890795Sgshapiro** smtptodsn() returns a pointer to a character string literal, 141990795Sgshapiro** which will remain valid forever, and thus does not need to 142090795Sgshapiro** be copied. Current code relies on this property. 142138032Speter*/ 142238032Speter 142338032Speterchar * 142438032Spetersmtptodsn(smtpstat) 142538032Speter int smtpstat; 142638032Speter{ 142738032Speter if (smtpstat < 0) 142838032Speter return "4.4.2"; 142938032Speter 143038032Speter switch (smtpstat) 143138032Speter { 143238032Speter case 450: /* Req mail action not taken: mailbox unavailable */ 143338032Speter return "4.2.0"; 143438032Speter 143538032Speter case 451: /* Req action aborted: local error in processing */ 143638032Speter return "4.3.0"; 143738032Speter 143838032Speter case 452: /* Req action not taken: insufficient sys storage */ 143938032Speter return "4.3.1"; 144038032Speter 144138032Speter case 500: /* Syntax error, command unrecognized */ 144238032Speter return "5.5.2"; 144338032Speter 144438032Speter case 501: /* Syntax error in parameters or arguments */ 144538032Speter return "5.5.4"; 144638032Speter 144738032Speter case 502: /* Command not implemented */ 144838032Speter return "5.5.1"; 144938032Speter 145038032Speter case 503: /* Bad sequence of commands */ 145138032Speter return "5.5.1"; 145238032Speter 145338032Speter case 504: /* Command parameter not implemented */ 145438032Speter return "5.5.4"; 145538032Speter 145638032Speter case 550: /* Req mail action not taken: mailbox unavailable */ 145738032Speter return "5.2.0"; 145838032Speter 145938032Speter case 551: /* User not local; please try <...> */ 146038032Speter return "5.1.6"; 146138032Speter 146238032Speter case 552: /* Req mail action aborted: exceeded storage alloc */ 146338032Speter return "5.2.2"; 146438032Speter 146538032Speter case 553: /* Req action not taken: mailbox name not allowed */ 146638032Speter return "5.1.0"; 146738032Speter 146838032Speter case 554: /* Transaction failed */ 146938032Speter return "5.0.0"; 147038032Speter } 147138032Speter 1472157006Sgshapiro if (REPLYTYPE(smtpstat) == 2) 147338032Speter return "2.0.0"; 1474157006Sgshapiro if (REPLYTYPE(smtpstat) == 4) 147538032Speter return "4.0.0"; 147638032Speter return "5.0.0"; 147738032Speter} 147890795Sgshapiro/* 147938032Speter** XTEXTIFY -- take regular text and turn it into DSN-style xtext 148038032Speter** 148138032Speter** Parameters: 148238032Speter** t -- the text to convert. 148338032Speter** taboo -- additional characters that must be encoded. 148438032Speter** 148538032Speter** Returns: 148638032Speter** The xtext-ified version of the same string. 148738032Speter*/ 148838032Speter 148938032Speterchar * 149038032Speterxtextify(t, taboo) 149138032Speter register char *t; 149238032Speter char *taboo; 149338032Speter{ 149438032Speter register char *p; 149538032Speter int l; 149638032Speter int nbogus; 149738032Speter static char *bp = NULL; 149838032Speter static int bplen = 0; 149938032Speter 150038032Speter if (taboo == NULL) 150138032Speter taboo = ""; 150238032Speter 150338032Speter /* figure out how long this xtext will have to be */ 150438032Speter nbogus = l = 0; 150538032Speter for (p = t; *p != '\0'; p++) 150638032Speter { 150738032Speter register int c = (*p & 0xff); 150838032Speter 150938032Speter /* ASCII dependence here -- this is the way the spec words it */ 151038032Speter if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || 151138032Speter strchr(taboo, c) != NULL) 151238032Speter nbogus++; 151338032Speter l++; 151438032Speter } 151590795Sgshapiro if (nbogus < 0) 151690795Sgshapiro { 151790795Sgshapiro /* since nbogus is ssize_t and wrapped, 2 * size_t would wrap */ 151890795Sgshapiro syserr("!xtextify string too long"); 151990795Sgshapiro } 152038032Speter if (nbogus == 0) 152138032Speter return t; 152238032Speter l += nbogus * 2 + 1; 152338032Speter 152438032Speter /* now allocate space if necessary for the new string */ 152538032Speter if (l > bplen) 152638032Speter { 152738032Speter if (bp != NULL) 152890795Sgshapiro sm_free(bp); /* XXX */ 152990795Sgshapiro bp = sm_pmalloc_x(l); 153038032Speter bplen = l; 153138032Speter } 153238032Speter 153338032Speter /* ok, copy the text with byte expansion */ 153438032Speter for (p = bp; *t != '\0'; ) 153538032Speter { 153638032Speter register int c = (*t++ & 0xff); 153738032Speter 153838032Speter /* ASCII dependence here -- this is the way the spec words it */ 153938032Speter if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || 154038032Speter strchr(taboo, c) != NULL) 154138032Speter { 154238032Speter *p++ = '+'; 154364565Sgshapiro *p++ = "0123456789ABCDEF"[c >> 4]; 154464565Sgshapiro *p++ = "0123456789ABCDEF"[c & 0xf]; 154538032Speter } 154638032Speter else 154738032Speter *p++ = c; 154838032Speter } 154938032Speter *p = '\0'; 155038032Speter return bp; 155138032Speter} 155290795Sgshapiro/* 155338032Speter** XUNTEXTIFY -- take xtext and turn it into plain text 155438032Speter** 155538032Speter** Parameters: 155638032Speter** t -- the xtextified text. 155738032Speter** 155838032Speter** Returns: 155938032Speter** The decoded text. No attempt is made to deal with 156038032Speter** null strings in the resulting text. 156138032Speter*/ 156238032Speter 156338032Speterchar * 156438032Speterxuntextify(t) 156538032Speter register char *t; 156638032Speter{ 156738032Speter register char *p; 156838032Speter int l; 156938032Speter static char *bp = NULL; 157038032Speter static int bplen = 0; 157138032Speter 157238032Speter /* heuristic -- if no plus sign, just return the input */ 157338032Speter if (strchr(t, '+') == NULL) 157438032Speter return t; 157538032Speter 157638032Speter /* xtext is always longer than decoded text */ 157738032Speter l = strlen(t); 157838032Speter if (l > bplen) 157938032Speter { 158038032Speter if (bp != NULL) 158190795Sgshapiro sm_free(bp); /* XXX */ 158238032Speter bp = xalloc(l); 158338032Speter bplen = l; 158438032Speter } 158538032Speter 158638032Speter /* ok, copy the text with byte compression */ 158738032Speter for (p = bp; *t != '\0'; t++) 158838032Speter { 158938032Speter register int c = *t & 0xff; 159038032Speter 159138032Speter if (c != '+') 159238032Speter { 159338032Speter *p++ = c; 159438032Speter continue; 159538032Speter } 159638032Speter 159738032Speter c = *++t & 0xff; 159838032Speter if (!isascii(c) || !isxdigit(c)) 159938032Speter { 160038032Speter /* error -- first digit is not hex */ 160138032Speter usrerr("bogus xtext: +%c", c); 160238032Speter t--; 160338032Speter continue; 160438032Speter } 160538032Speter if (isdigit(c)) 160638032Speter c -= '0'; 160738032Speter else if (isupper(c)) 160838032Speter c -= 'A' - 10; 160938032Speter else 161038032Speter c -= 'a' - 10; 161138032Speter *p = c << 4; 161238032Speter 161338032Speter c = *++t & 0xff; 161438032Speter if (!isascii(c) || !isxdigit(c)) 161538032Speter { 161638032Speter /* error -- second digit is not hex */ 161738032Speter usrerr("bogus xtext: +%x%c", *p >> 4, c); 161838032Speter t--; 161938032Speter continue; 162038032Speter } 162138032Speter if (isdigit(c)) 162238032Speter c -= '0'; 162338032Speter else if (isupper(c)) 162438032Speter c -= 'A' - 10; 162538032Speter else 162638032Speter c -= 'a' - 10; 162738032Speter *p++ |= c; 162838032Speter } 162938032Speter *p = '\0'; 163038032Speter return bp; 163138032Speter} 163290795Sgshapiro/* 163338032Speter** XTEXTOK -- check if a string is legal xtext 163438032Speter** 163538032Speter** Xtext is used in Delivery Status Notifications. The spec was 163638032Speter** taken from RFC 1891, ``SMTP Service Extension for Delivery 163738032Speter** Status Notifications''. 163838032Speter** 163938032Speter** Parameters: 164038032Speter** s -- the string to check. 164138032Speter** 164238032Speter** Returns: 164390795Sgshapiro** true -- if 's' is legal xtext. 164490795Sgshapiro** false -- if it has any illegal characters in it. 164538032Speter*/ 164638032Speter 164738032Speterbool 164838032Speterxtextok(s) 164938032Speter char *s; 165038032Speter{ 165138032Speter int c; 165238032Speter 165338032Speter while ((c = *s++) != '\0') 165438032Speter { 165538032Speter if (c == '+') 165638032Speter { 165738032Speter c = *s++; 165838032Speter if (!isascii(c) || !isxdigit(c)) 165990795Sgshapiro return false; 166038032Speter c = *s++; 166138032Speter if (!isascii(c) || !isxdigit(c)) 166290795Sgshapiro return false; 166338032Speter } 166438032Speter else if (c < '!' || c > '~' || c == '=') 166590795Sgshapiro return false; 166638032Speter } 166790795Sgshapiro return true; 166838032Speter} 166990795Sgshapiro/* 167038032Speter** PRUNEROUTE -- prune an RFC-822 source route 167164565Sgshapiro** 167238032Speter** Trims down a source route to the last internet-registered hop. 167338032Speter** This is encouraged by RFC 1123 section 5.3.3. 167464565Sgshapiro** 167538032Speter** Parameters: 167638032Speter** addr -- the address 167764565Sgshapiro** 167838032Speter** Returns: 167990795Sgshapiro** true -- address was modified 168090795Sgshapiro** false -- address could not be pruned 168164565Sgshapiro** 168238032Speter** Side Effects: 168338032Speter** modifies addr in-place 168438032Speter*/ 168538032Speter 168664565Sgshapirostatic bool 168738032Speterpruneroute(addr) 168838032Speter char *addr; 168938032Speter{ 169038032Speter#if NAMED_BIND 169138032Speter char *start, *at, *comma; 169238032Speter char c; 169390795Sgshapiro int braclev; 169438032Speter int rcode; 169538032Speter int i; 169638032Speter char hostbuf[BUFSIZ]; 169738032Speter char *mxhosts[MAXMXHOSTS + 1]; 169838032Speter 169938032Speter /* check to see if this is really a route-addr */ 170038032Speter if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') 170190795Sgshapiro return false; 170290795Sgshapiro 170390795Sgshapiro /* 170490795Sgshapiro ** Can't simply find the first ':' is the address might be in the 170590795Sgshapiro ** form: "<@[IPv6:::1]:user@host>" and the first ':' in inside 170690795Sgshapiro ** the IPv6 address. 170790795Sgshapiro */ 170890795Sgshapiro 170990795Sgshapiro start = addr; 171090795Sgshapiro braclev = 0; 171190795Sgshapiro while (*start != '\0') 171290795Sgshapiro { 171390795Sgshapiro if (*start == ':' && braclev <= 0) 171490795Sgshapiro break; 171590795Sgshapiro else if (*start == '[') 171690795Sgshapiro braclev++; 171790795Sgshapiro else if (*start == ']' && braclev > 0) 171890795Sgshapiro braclev--; 171990795Sgshapiro start++; 172090795Sgshapiro } 172190795Sgshapiro if (braclev > 0 || *start != ':') 172290795Sgshapiro return false; 172390795Sgshapiro 172438032Speter at = strrchr(addr, '@'); 172590795Sgshapiro if (at == NULL || at < start) 172690795Sgshapiro return false; 172738032Speter 172838032Speter /* slice off the angle brackets */ 172938032Speter i = strlen(at + 1); 173090795Sgshapiro if (i >= sizeof hostbuf) 173190795Sgshapiro return false; 173290795Sgshapiro (void) sm_strlcpy(hostbuf, at + 1, sizeof hostbuf); 173338032Speter hostbuf[i - 1] = '\0'; 173438032Speter 173590795Sgshapiro while (start != NULL) 173638032Speter { 173790795Sgshapiro if (getmxrr(hostbuf, mxhosts, NULL, false, 173890795Sgshapiro &rcode, true, NULL) > 0) 173938032Speter { 174090795Sgshapiro (void) sm_strlcpy(addr + 1, start + 1, 174190795Sgshapiro strlen(addr) - 1); 174290795Sgshapiro return true; 174338032Speter } 174438032Speter c = *start; 174538032Speter *start = '\0'; 174638032Speter comma = strrchr(addr, ','); 174738032Speter if (comma != NULL && comma[1] == '@' && 174890795Sgshapiro strlen(comma + 2) < sizeof hostbuf) 174990795Sgshapiro (void) sm_strlcpy(hostbuf, comma + 2, sizeof hostbuf); 175038032Speter else 175138032Speter comma = NULL; 175238032Speter *start = c; 175338032Speter start = comma; 175438032Speter } 175564565Sgshapiro#endif /* NAMED_BIND */ 175690795Sgshapiro return false; 175738032Speter} 1758