138032Speter/* 2261194Sgshapiro * Copyright (c) 1998-2003, 2006, 2012, 2013 Proofpoint, Inc. and its suppliers. 364565Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1464565Sgshapiro#include <sendmail.h> 1564565Sgshapiro 16266527SgshapiroSM_RCSID("@(#)$Id: savemail.c,v 8.319 2013-11-22 20:51:56 ca Exp $") 1764565Sgshapiro 18157006Sgshapirostatic bool errbody __P((MCI *, ENVELOPE *, char *)); 1964565Sgshapirostatic bool pruneroute __P((char *)); 2064565Sgshapiro 2138032Speter/* 2238032Speter** SAVEMAIL -- Save mail on error 2338032Speter** 2438032Speter** If mailing back errors, mail it back to the originator 2538032Speter** together with an error message; otherwise, just put it in 2638032Speter** dead.letter in the user's home directory (if he exists on 2738032Speter** this machine). 2838032Speter** 2938032Speter** Parameters: 3038032Speter** e -- the envelope containing the message in error. 3190795Sgshapiro** sendbody -- if true, also send back the body of the 3238032Speter** message; otherwise just send the header. 3338032Speter** 3438032Speter** Returns: 3590795Sgshapiro** true if savemail panic'ed, (i.e., the data file should 3690795Sgshapiro** be preserved by dropenvelope()) 3738032Speter** 3838032Speter** Side Effects: 3938032Speter** Saves the letter, by writing or mailing it back to the 4038032Speter** sender, or by putting it in dead.letter in her home 4138032Speter** directory. 4238032Speter*/ 4338032Speter 4438032Speter/* defines for state machine */ 4564565Sgshapiro#define ESM_REPORT 0 /* report to sender's terminal */ 4664565Sgshapiro#define ESM_MAIL 1 /* mail back to sender */ 4764565Sgshapiro#define ESM_QUIET 2 /* mail has already been returned */ 4864565Sgshapiro#define ESM_DEADLETTER 3 /* save in ~/dead.letter */ 4964565Sgshapiro#define ESM_POSTMASTER 4 /* return to postmaster */ 5064565Sgshapiro#define ESM_DEADLETTERDROP 5 /* save in DeadLetterDrop */ 5164565Sgshapiro#define ESM_PANIC 6 /* call loseqfile() */ 5264565Sgshapiro#define ESM_DONE 7 /* message is successfully delivered */ 5338032Speter 5490795Sgshapirobool 5538032Spetersavemail(e, sendbody) 5638032Speter register ENVELOPE *e; 5738032Speter bool sendbody; 5838032Speter{ 5990795Sgshapiro register SM_FILE_T *fp; 6090795Sgshapiro bool panic = false; 6138032Speter int state; 6238032Speter auto ADDRESS *q = NULL; 6338032Speter register char *p; 6438032Speter MCI mcibuf; 6538032Speter int flags; 6664565Sgshapiro long sff; 6764565Sgshapiro char buf[MAXLINE + 1]; 6898125Sgshapiro char dlbuf[MAXPATHLEN]; 6990795Sgshapiro SM_MBDB_T user; 7038032Speter 7190795Sgshapiro 7238032Speter if (tTd(6, 1)) 7338032Speter { 7490795Sgshapiro sm_dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=", 7538032Speter e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id, 7638032Speter ExitStat); 77132946Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 7838032Speter } 7938032Speter 8038032Speter if (e->e_id == NULL) 8138032Speter { 8238032Speter /* can't return a message with no id */ 8390795Sgshapiro return panic; 8438032Speter } 8538032Speter 8638032Speter /* 8738032Speter ** In the unhappy event we don't know who to return the mail 8838032Speter ** to, make someone up. 8938032Speter */ 9038032Speter 9138032Speter if (e->e_from.q_paddr == NULL) 9238032Speter { 9338032Speter e->e_sender = "Postmaster"; 9438032Speter if (parseaddr(e->e_sender, &e->e_from, 9590795Sgshapiro RF_COPYPARSE|RF_SENDERADDR, 9690795Sgshapiro '\0', NULL, e, false) == NULL) 9738032Speter { 9864565Sgshapiro syserr("553 5.3.5 Cannot parse Postmaster!"); 9990795Sgshapiro finis(true, true, EX_SOFTWARE); 10038032Speter } 10138032Speter } 10238032Speter e->e_to = NULL; 10338032Speter 10438032Speter /* 10538032Speter ** Basic state machine. 10638032Speter ** 10738032Speter ** This machine runs through the following states: 10838032Speter ** 10938032Speter ** ESM_QUIET Errors have already been printed iff the 11038032Speter ** sender is local. 11138032Speter ** ESM_REPORT Report directly to the sender's terminal. 11238032Speter ** ESM_MAIL Mail response to the sender. 11338032Speter ** ESM_DEADLETTER Save response in ~/dead.letter. 11438032Speter ** ESM_POSTMASTER Mail response to the postmaster. 11564565Sgshapiro ** ESM_DEADLETTERDROP 11664565Sgshapiro ** If DeadLetterDrop set, save it there. 11738032Speter ** ESM_PANIC Save response anywhere possible. 11838032Speter */ 11938032Speter 12038032Speter /* determine starting state */ 12138032Speter switch (e->e_errormode) 12238032Speter { 12338032Speter case EM_WRITE: 12438032Speter state = ESM_REPORT; 12538032Speter break; 12638032Speter 12738032Speter case EM_BERKNET: 12838032Speter case EM_MAIL: 12938032Speter state = ESM_MAIL; 13038032Speter break; 13138032Speter 13238032Speter case EM_PRINT: 13338032Speter case '\0': 13438032Speter state = ESM_QUIET; 13538032Speter break; 13638032Speter 13738032Speter case EM_QUIET: 13838032Speter /* no need to return anything at all */ 13990795Sgshapiro return panic; 14038032Speter 14138032Speter default: 14290795Sgshapiro syserr("554 5.3.0 savemail: bogus errormode x%x", 14364565Sgshapiro e->e_errormode); 14438032Speter state = ESM_MAIL; 14538032Speter break; 14638032Speter } 14738032Speter 14838032Speter /* if this is already an error response, send to postmaster */ 14938032Speter if (bitset(EF_RESPONSE, e->e_flags)) 15038032Speter { 15138032Speter if (e->e_parent != NULL && 15238032Speter bitset(EF_RESPONSE, e->e_parent->e_flags)) 15338032Speter { 15438032Speter /* got an error sending a response -- can it */ 15590795Sgshapiro return panic; 15638032Speter } 15738032Speter state = ESM_POSTMASTER; 15838032Speter } 15938032Speter 16038032Speter while (state != ESM_DONE) 16138032Speter { 16238032Speter if (tTd(6, 5)) 16390795Sgshapiro sm_dprintf(" state %d\n", state); 16438032Speter 16538032Speter switch (state) 16638032Speter { 16738032Speter case ESM_QUIET: 16838032Speter if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags)) 16938032Speter state = ESM_DEADLETTER; 17038032Speter else 17138032Speter state = ESM_MAIL; 17238032Speter break; 17338032Speter 17438032Speter case ESM_REPORT: 17538032Speter 17638032Speter /* 17738032Speter ** If the user is still logged in on the same terminal, 17838032Speter ** then write the error messages back to hir (sic). 17938032Speter */ 18038032Speter 181132946Sgshapiro#if USE_TTYPATH 18238032Speter p = ttypath(); 183363466Sgshapiro#else 184132946Sgshapiro p = NULL; 185363466Sgshapiro#endif 186132946Sgshapiro 18790795Sgshapiro if (p == NULL || sm_io_reopen(SmFtStdio, 18890795Sgshapiro SM_TIME_DEFAULT, 18990795Sgshapiro p, SM_IO_WRONLY, NULL, 19090795Sgshapiro smioout) == NULL) 19138032Speter { 19238032Speter state = ESM_MAIL; 19338032Speter break; 19438032Speter } 19538032Speter 196168520Sgshapiro expand("\201n", buf, sizeof(buf), e); 19790795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 19890795Sgshapiro "\r\nMessage from %s...\r\n", buf); 19990795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 20090795Sgshapiro "Errors occurred while sending mail.\r\n"); 20138032Speter if (e->e_xfp != NULL) 20238032Speter { 20364565Sgshapiro (void) bfrewind(e->e_xfp); 20490795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 20590795Sgshapiro "Transcript follows:\r\n"); 20690795Sgshapiro while (sm_io_fgets(e->e_xfp, SM_TIME_DEFAULT, 207249729Sgshapiro buf, sizeof(buf)) >= 0 && 20890795Sgshapiro !sm_io_error(smioout)) 20990795Sgshapiro (void) sm_io_fputs(smioout, 21090795Sgshapiro SM_TIME_DEFAULT, 21190795Sgshapiro buf); 21238032Speter } 21338032Speter else 21438032Speter { 21590795Sgshapiro syserr("Cannot open %s", 21690795Sgshapiro queuename(e, XSCRPT_LETTER)); 21790795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 21890795Sgshapiro "Transcript of session is unavailable.\r\n"); 21938032Speter } 22090795Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 22190795Sgshapiro "Original message will be saved in dead.letter.\r\n"); 22238032Speter state = ESM_DEADLETTER; 22338032Speter break; 22438032Speter 22538032Speter case ESM_MAIL: 22638032Speter /* 22738032Speter ** If mailing back, do it. 22838032Speter ** Throw away all further output. Don't alias, 22938032Speter ** since this could cause loops, e.g., if joe 23038032Speter ** mails to joe@x, and for some reason the network 23138032Speter ** for @x is down, then the response gets sent to 23238032Speter ** joe@x, which gives a response, etc. Also force 23338032Speter ** the mail to be delivered even if a version of 23438032Speter ** it has already been sent to the sender. 23538032Speter ** 23638032Speter ** If this is a configuration or local software 23738032Speter ** error, send to the local postmaster as well, 23838032Speter ** since the originator can't do anything 23938032Speter ** about it anyway. Note that this is a full 24038032Speter ** copy of the message (intentionally) so that 24138032Speter ** the Postmaster can forward things along. 24238032Speter */ 24338032Speter 24438032Speter if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE) 24538032Speter { 24690795Sgshapiro (void) sendtolist("postmaster", NULLADDR, 24790795Sgshapiro &e->e_errorqueue, 0, e); 24838032Speter } 24938032Speter if (!emptyaddr(&e->e_from)) 25038032Speter { 25138032Speter char from[TOBUFSIZE]; 25238032Speter 25390795Sgshapiro if (sm_strlcpy(from, e->e_from.q_paddr, 254168520Sgshapiro sizeof(from)) >= sizeof(from)) 25538032Speter { 25638032Speter state = ESM_POSTMASTER; 25738032Speter break; 25838032Speter } 25938032Speter 26090795Sgshapiro if (!DontPruneRoutes) 26190795Sgshapiro (void) pruneroute(from); 26238032Speter 26338032Speter (void) sendtolist(from, NULLADDR, 26438032Speter &e->e_errorqueue, 0, e); 26538032Speter } 26638032Speter 26738032Speter /* 26838032Speter ** Deliver a non-delivery report to the 26938032Speter ** Postmaster-designate (not necessarily 27038032Speter ** Postmaster). This does not include the 27138032Speter ** body of the message, for privacy reasons. 27238032Speter ** You really shouldn't need this. 27338032Speter */ 27438032Speter 27538032Speter e->e_flags |= EF_PM_NOTIFY; 27638032Speter 27738032Speter /* check to see if there are any good addresses */ 27838032Speter for (q = e->e_errorqueue; q != NULL; q = q->q_next) 27964565Sgshapiro { 28064565Sgshapiro if (QS_IS_SENDABLE(q->q_state)) 28138032Speter break; 28264565Sgshapiro } 28338032Speter if (q == NULL) 28438032Speter { 28538032Speter /* this is an error-error */ 28638032Speter state = ESM_POSTMASTER; 28738032Speter break; 28838032Speter } 28938032Speter if (returntosender(e->e_message, e->e_errorqueue, 29038032Speter sendbody ? RTSF_SEND_BODY 29138032Speter : RTSF_NO_BODY, 29238032Speter e) == 0) 29338032Speter { 29438032Speter state = ESM_DONE; 29538032Speter break; 29638032Speter } 29738032Speter 29838032Speter /* didn't work -- return to postmaster */ 29938032Speter state = ESM_POSTMASTER; 30038032Speter break; 30138032Speter 30238032Speter case ESM_POSTMASTER: 30338032Speter /* 30438032Speter ** Similar to previous case, but to system postmaster. 30538032Speter */ 30638032Speter 30738032Speter q = NULL; 308168520Sgshapiro expand(DoubleBounceAddr, buf, sizeof(buf), e); 30990795Sgshapiro 31090795Sgshapiro /* 31190795Sgshapiro ** Just drop it on the floor if DoubleBounceAddr 31290795Sgshapiro ** expands to an empty string. 31390795Sgshapiro */ 31490795Sgshapiro 31590795Sgshapiro if (*buf == '\0') 31690795Sgshapiro { 31790795Sgshapiro state = ESM_DONE; 31890795Sgshapiro break; 31990795Sgshapiro } 32064565Sgshapiro if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0) 32138032Speter { 32264565Sgshapiro syserr("553 5.3.0 cannot parse %s!", buf); 32338032Speter ExitStat = EX_SOFTWARE; 32464565Sgshapiro state = ESM_DEADLETTERDROP; 32538032Speter break; 32638032Speter } 32738032Speter flags = RTSF_PM_BOUNCE; 32838032Speter if (sendbody) 32938032Speter flags |= RTSF_SEND_BODY; 33038032Speter if (returntosender(e->e_message, q, flags, e) == 0) 33138032Speter { 33238032Speter state = ESM_DONE; 33338032Speter break; 33438032Speter } 33538032Speter 33638032Speter /* didn't work -- last resort */ 33764565Sgshapiro state = ESM_DEADLETTERDROP; 33838032Speter break; 33938032Speter 34038032Speter case ESM_DEADLETTER: 34138032Speter /* 34238032Speter ** Save the message in dead.letter. 34338032Speter ** If we weren't mailing back, and the user is 34438032Speter ** local, we should save the message in 34538032Speter ** ~/dead.letter so that the poor person doesn't 34638032Speter ** have to type it over again -- and we all know 34738032Speter ** what poor typists UNIX users are. 34838032Speter */ 34938032Speter 35038032Speter p = NULL; 35138032Speter if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags)) 35238032Speter { 35338032Speter if (e->e_from.q_home != NULL) 35438032Speter p = e->e_from.q_home; 35590795Sgshapiro else if (sm_mbdb_lookup(e->e_from.q_user, &user) 35690795Sgshapiro == EX_OK && 35790795Sgshapiro *user.mbdb_homedir != '\0') 35890795Sgshapiro p = user.mbdb_homedir; 35938032Speter } 36038032Speter if (p == NULL || e->e_dfp == NULL) 36138032Speter { 36238032Speter /* no local directory or no data file */ 36338032Speter state = ESM_MAIL; 36438032Speter break; 36538032Speter } 36638032Speter 36738032Speter /* we have a home directory; write dead.letter */ 36890795Sgshapiro macdefine(&e->e_macro, A_TEMP, 'z', p); 36964565Sgshapiro 37064565Sgshapiro /* get the sender for the UnixFromLine */ 37164565Sgshapiro p = macvalue('g', e); 37290795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); 37364565Sgshapiro 374168520Sgshapiro expand("\201z/dead.letter", dlbuf, sizeof(dlbuf), e); 37564565Sgshapiro sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; 37638032Speter if (RealUid == 0) 37764565Sgshapiro sff |= SFF_ROOTOK; 37898125Sgshapiro e->e_to = dlbuf; 37998125Sgshapiro if (writable(dlbuf, NULL, sff) && 38098125Sgshapiro mailfile(dlbuf, FileMailer, NULL, sff, e) == EX_OK) 38138032Speter { 38238032Speter int oldverb = Verbose; 38338032Speter 38464565Sgshapiro if (OpMode != MD_DAEMON && OpMode != MD_SMTP) 38564565Sgshapiro Verbose = 1; 38664565Sgshapiro if (Verbose > 0) 38798125Sgshapiro message("Saved message in %s", dlbuf); 38838032Speter Verbose = oldverb; 38990795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', p); 39038032Speter state = ESM_DONE; 39138032Speter break; 39238032Speter } 39390795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', p); 39438032Speter state = ESM_MAIL; 39538032Speter break; 39638032Speter 39764565Sgshapiro case ESM_DEADLETTERDROP: 39838032Speter /* 39964565Sgshapiro ** Log the mail in DeadLetterDrop file. 40038032Speter */ 40138032Speter 40238032Speter if (e->e_class < 0) 40338032Speter { 40438032Speter state = ESM_DONE; 40538032Speter break; 40638032Speter } 40738032Speter 40838032Speter if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') || 40964565Sgshapiro DeadLetterDrop == NULL || 41064565Sgshapiro DeadLetterDrop[0] == '\0') 41138032Speter { 41238032Speter state = ESM_PANIC; 41338032Speter break; 41438032Speter } 41538032Speter 41664565Sgshapiro sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN; 41764565Sgshapiro if (!writable(DeadLetterDrop, NULL, sff) || 41838032Speter (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND, 41964565Sgshapiro FileMode, sff)) == NULL) 42038032Speter { 42138032Speter state = ESM_PANIC; 42238032Speter break; 42338032Speter } 42438032Speter 425168520Sgshapiro memset(&mcibuf, '\0', sizeof(mcibuf)); 42638032Speter mcibuf.mci_out = fp; 42738032Speter mcibuf.mci_mailer = FileMailer; 42838032Speter if (bitnset(M_7BITS, FileMailer->m_flags)) 42938032Speter mcibuf.mci_flags |= MCIF_7BIT; 43038032Speter 43164565Sgshapiro /* get the sender for the UnixFromLine */ 43264565Sgshapiro p = macvalue('g', e); 43390795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); 43464565Sgshapiro 435157006Sgshapiro if (!putfromline(&mcibuf, e) || 436157006Sgshapiro !(*e->e_puthdr)(&mcibuf, e->e_header, e, 437157006Sgshapiro M87F_OUTER) || 438157006Sgshapiro !(*e->e_putbody)(&mcibuf, e, NULL) || 439157006Sgshapiro !putline("\n", &mcibuf) || 440157006Sgshapiro sm_io_flush(fp, SM_TIME_DEFAULT) == SM_IO_EOF || 441157006Sgshapiro sm_io_error(fp) || 44290795Sgshapiro sm_io_close(fp, SM_TIME_DEFAULT) < 0) 44338032Speter state = ESM_PANIC; 44438032Speter else 44538032Speter { 44638032Speter int oldverb = Verbose; 44738032Speter 44864565Sgshapiro if (OpMode != MD_DAEMON && OpMode != MD_SMTP) 44964565Sgshapiro Verbose = 1; 45064565Sgshapiro if (Verbose > 0) 45164565Sgshapiro message("Saved message in %s", 45264565Sgshapiro DeadLetterDrop); 45338032Speter Verbose = oldverb; 45438032Speter if (LogLevel > 3) 45538032Speter sm_syslog(LOG_NOTICE, e->e_id, 45664565Sgshapiro "Saved message in %s", 45764565Sgshapiro DeadLetterDrop); 45838032Speter state = ESM_DONE; 45938032Speter } 46090795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', p); 46138032Speter break; 46238032Speter 46338032Speter default: 46464565Sgshapiro syserr("554 5.3.5 savemail: unknown state %d", state); 46564565Sgshapiro /* FALLTHROUGH */ 46638032Speter 46738032Speter case ESM_PANIC: 46838032Speter /* leave the locked queue & transcript files around */ 46938032Speter loseqfile(e, "savemail panic"); 47090795Sgshapiro panic = true; 47166497Sgshapiro errno = 0; 47290795Sgshapiro syserr("554 savemail: cannot save rejected email anywhere"); 47390795Sgshapiro state = ESM_DONE; 47490795Sgshapiro break; 47538032Speter } 47638032Speter } 47790795Sgshapiro return panic; 47838032Speter} 47990795Sgshapiro/* 48038032Speter** RETURNTOSENDER -- return a message to the sender with an error. 48138032Speter** 48238032Speter** Parameters: 48338032Speter** msg -- the explanatory message. 48438032Speter** returnq -- the queue of people to send the message to. 48538032Speter** flags -- flags tweaking the operation: 48638032Speter** RTSF_SENDBODY -- include body of message (otherwise 48738032Speter** just send the header). 48838032Speter** RTSF_PMBOUNCE -- this is a postmaster bounce. 48938032Speter** e -- the current envelope. 49038032Speter** 49138032Speter** Returns: 49238032Speter** zero -- if everything went ok. 49338032Speter** else -- some error. 49438032Speter** 49538032Speter** Side Effects: 49690795Sgshapiro** Returns the current message to the sender via mail. 49738032Speter*/ 49838032Speter 49938032Speter#define MAXRETURNS 6 /* max depth of returning messages */ 50090795Sgshapiro#define ERRORFUDGE 1024 /* nominal size of error message text */ 50138032Speter 50238032Speterint 50338032Speterreturntosender(msg, returnq, flags, e) 50438032Speter char *msg; 50538032Speter ADDRESS *returnq; 50638032Speter int flags; 50738032Speter register ENVELOPE *e; 50838032Speter{ 509244833Sgshapiro int ret; 51038032Speter register ENVELOPE *ee; 51138032Speter ENVELOPE *oldcur = CurEnv; 51238032Speter ENVELOPE errenvelope; 51338032Speter static int returndepth = 0; 51438032Speter register ADDRESS *q; 51538032Speter char *p; 51638032Speter char buf[MAXNAME + 1]; 51738032Speter 51838032Speter if (returnq == NULL) 51964565Sgshapiro return -1; 52038032Speter 52138032Speter if (msg == NULL) 52238032Speter msg = "Unable to deliver mail"; 52338032Speter 52438032Speter if (tTd(6, 1)) 52538032Speter { 52690795Sgshapiro sm_dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%p, returnq=", 527363466Sgshapiro msg, returndepth, (void *)e); 528132946Sgshapiro printaddr(sm_debug_file(), returnq, true); 52938032Speter if (tTd(6, 20)) 53038032Speter { 53190795Sgshapiro sm_dprintf("Sendq="); 532132946Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 53338032Speter } 53438032Speter } 53538032Speter 53638032Speter if (++returndepth >= MAXRETURNS) 53738032Speter { 53838032Speter if (returndepth != MAXRETURNS) 53964565Sgshapiro syserr("554 5.3.0 returntosender: infinite recursion on %s", 54064565Sgshapiro returnq->q_paddr); 54138032Speter /* don't "unrecurse" and fake a clean exit */ 54238032Speter /* returndepth--; */ 54364565Sgshapiro return 0; 54438032Speter } 54538032Speter 54690795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); 54790795Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', NULL); 54838032Speter 54938032Speter /* initialize error envelope */ 55090795Sgshapiro ee = newenvelope(&errenvelope, e, sm_rpool_new_x(NULL)); 55190795Sgshapiro macdefine(&ee->e_macro, A_PERM, 'a', "\201b"); 55290795Sgshapiro macdefine(&ee->e_macro, A_PERM, 'r', ""); 55390795Sgshapiro macdefine(&ee->e_macro, A_PERM, 's', "localhost"); 55490795Sgshapiro macdefine(&ee->e_macro, A_PERM, '_', "localhost"); 555110563Sgshapiro clrsessenvelope(ee); 55664565Sgshapiro 55738032Speter ee->e_puthdr = putheader; 55838032Speter ee->e_putbody = errbody; 55938032Speter ee->e_flags |= EF_RESPONSE|EF_METOO; 56038032Speter if (!bitset(EF_OLDSTYLE, e->e_flags)) 56138032Speter ee->e_flags &= ~EF_OLDSTYLE; 56264565Sgshapiro if (bitset(EF_DONT_MIME, e->e_flags)) 56364565Sgshapiro { 56464565Sgshapiro ee->e_flags |= EF_DONT_MIME; 56564565Sgshapiro 56664565Sgshapiro /* 56764565Sgshapiro ** If we can't convert to MIME and we don't pass 56864565Sgshapiro ** 8-bit, we can't send the body. 56964565Sgshapiro */ 57064565Sgshapiro 57164565Sgshapiro if (bitset(EF_HAS8BIT, e->e_flags) && 57264565Sgshapiro !bitset(MM_PASS8BIT, MimeMode)) 57364565Sgshapiro flags &= ~RTSF_SEND_BODY; 57464565Sgshapiro } 57564565Sgshapiro 57638032Speter ee->e_sendqueue = returnq; 57790795Sgshapiro ee->e_msgsize = 0; 57864565Sgshapiro if (bitset(RTSF_SEND_BODY, flags) && 57964565Sgshapiro !bitset(PRIV_NOBODYRETN, PrivacyFlags)) 58090795Sgshapiro ee->e_msgsize = ERRORFUDGE + e->e_msgsize; 58138032Speter else 58238032Speter ee->e_flags |= EF_NO_BODY_RETN; 58390795Sgshapiro 584285229Sgshapiro#if _FFR_BOUNCE_QUEUE 585285229Sgshapiro if (BounceQueue != NOQGRP) 586285229Sgshapiro ee->e_qgrp = ee->e_dfqgrp = BounceQueue; 587363466Sgshapiro#endif 58890795Sgshapiro if (!setnewqueue(ee)) 58990795Sgshapiro { 59090795Sgshapiro syserr("554 5.3.0 returntosender: cannot select queue for %s", 59190795Sgshapiro returnq->q_paddr); 59290795Sgshapiro ExitStat = EX_UNAVAILABLE; 59390795Sgshapiro returndepth--; 59490795Sgshapiro return -1; 59590795Sgshapiro } 59638032Speter initsys(ee); 59764565Sgshapiro 59864565Sgshapiro#if NAMED_BIND 59964565Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 60064565Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 601363466Sgshapiro#endif 60238032Speter for (q = returnq; q != NULL; q = q->q_next) 60338032Speter { 60464565Sgshapiro if (QS_IS_BADADDR(q->q_state)) 60538032Speter continue; 60638032Speter 60738032Speter q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 60838032Speter q->q_flags |= QPINGONFAILURE; 60938032Speter 61064565Sgshapiro if (!QS_IS_DEAD(q->q_state)) 61138032Speter ee->e_nrcpts++; 61238032Speter 61338032Speter if (q->q_alias == NULL) 614168520Sgshapiro addheader("To", q->q_paddr, 0, ee, true); 61538032Speter } 61638032Speter 61738032Speter if (LogLevel > 5) 61838032Speter { 61964565Sgshapiro if (bitset(EF_RESPONSE, e->e_flags)) 62038032Speter p = "return to sender"; 62164565Sgshapiro else if (bitset(EF_WARNING, e->e_flags)) 62264565Sgshapiro p = "sender notify"; 62338032Speter else if (bitset(RTSF_PM_BOUNCE, flags)) 62438032Speter p = "postmaster notify"; 62538032Speter else 62638032Speter p = "DSN"; 62790795Sgshapiro sm_syslog(LOG_INFO, e->e_id, "%s: %s: %s", 62864565Sgshapiro ee->e_id, p, shortenstring(msg, MAXSHORTSTR)); 62938032Speter } 63038032Speter 63138032Speter if (SendMIMEErrors) 63238032Speter { 633168520Sgshapiro addheader("MIME-Version", "1.0", 0, ee, true); 634168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s.%ld/%.100s", 63590795Sgshapiro ee->e_id, (long)curtime(), MyHostName); 63690795Sgshapiro ee->e_msgboundary = sm_rpool_strdup_x(ee->e_rpool, buf); 637168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 63838032Speter#if DSN 63964565Sgshapiro "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"", 640363466Sgshapiro#else 64164565Sgshapiro "multipart/mixed; boundary=\"%s\"", 642363466Sgshapiro#endif 64364565Sgshapiro ee->e_msgboundary); 644168520Sgshapiro addheader("Content-Type", buf, 0, ee, true); 64538032Speter 64638032Speter p = hvalue("Content-Transfer-Encoding", e->e_header); 64790795Sgshapiro if (p != NULL && sm_strcasecmp(p, "binary") != 0) 64838032Speter p = NULL; 64938032Speter if (p == NULL && bitset(EF_HAS8BIT, e->e_flags)) 65038032Speter p = "8bit"; 65138032Speter if (p != NULL) 652168520Sgshapiro addheader("Content-Transfer-Encoding", p, 0, ee, true); 65338032Speter } 65438032Speter if (strncmp(msg, "Warning:", 8) == 0) 65538032Speter { 656168520Sgshapiro addheader("Subject", msg, 0, ee, true); 65738032Speter p = "warning-timeout"; 65838032Speter } 65938032Speter else if (strncmp(msg, "Postmaster warning:", 19) == 0) 66038032Speter { 661168520Sgshapiro addheader("Subject", msg, 0, ee, true); 66238032Speter p = "postmaster-warning"; 66338032Speter } 66438032Speter else if (strcmp(msg, "Return receipt") == 0) 66538032Speter { 666168520Sgshapiro addheader("Subject", msg, 0, ee, true); 66738032Speter p = "return-receipt"; 66838032Speter } 66938032Speter else if (bitset(RTSF_PM_BOUNCE, flags)) 67038032Speter { 671168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 67264565Sgshapiro "Postmaster notify: see transcript for details"); 673168520Sgshapiro addheader("Subject", buf, 0, ee, true); 67438032Speter p = "postmaster-notification"; 67538032Speter } 67638032Speter else 67738032Speter { 678168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 67964565Sgshapiro "Returned mail: see transcript for details"); 680168520Sgshapiro addheader("Subject", buf, 0, ee, true); 68138032Speter p = "failure"; 68238032Speter } 683168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "auto-generated (%s)", p); 684168520Sgshapiro addheader("Auto-Submitted", buf, 0, ee, true); 68538032Speter 68638032Speter /* fake up an address header for the from person */ 687168520Sgshapiro expand("\201n", buf, sizeof(buf), e); 68864565Sgshapiro if (parseaddr(buf, &ee->e_from, 68990795Sgshapiro RF_COPYALL|RF_SENDERADDR, '\0', NULL, e, false) == NULL) 69038032Speter { 69164565Sgshapiro syserr("553 5.3.5 Can't parse myself!"); 69238032Speter ExitStat = EX_SOFTWARE; 69338032Speter returndepth--; 69464565Sgshapiro return -1; 69538032Speter } 69638032Speter ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 69738032Speter ee->e_from.q_flags |= QPINGONFAILURE; 69838032Speter ee->e_sender = ee->e_from.q_paddr; 69938032Speter 70038032Speter /* push state into submessage */ 70138032Speter CurEnv = ee; 70290795Sgshapiro macdefine(&ee->e_macro, A_PERM, 'f', "\201n"); 70390795Sgshapiro macdefine(&ee->e_macro, A_PERM, 'x', "Mail Delivery Subsystem"); 70490795Sgshapiro eatheader(ee, true, true); 70538032Speter 70638032Speter /* mark statistics */ 70790795Sgshapiro markstats(ee, NULLADDR, STATS_NORMAL); 70838032Speter 709285229Sgshapiro#if _FFR_BOUNCE_QUEUE 710285229Sgshapiro if (BounceQueue == NOQGRP) 711285229Sgshapiro { 712285229Sgshapiro#endif 713285229Sgshapiro /* actually deliver the error message */ 714285229Sgshapiro sendall(ee, SM_DELIVER); 715285229Sgshapiro#if _FFR_BOUNCE_QUEUE 716285229Sgshapiro } 717285229Sgshapiro#endif 718203004Sgshapiro (void) dropenvelope(ee, true, false); 71938032Speter 72038032Speter /* check for delivery errors */ 721244833Sgshapiro ret = -1; 72264565Sgshapiro if (ee->e_parent == NULL || 72364565Sgshapiro !bitset(EF_RESPONSE, ee->e_parent->e_flags)) 72438032Speter { 725244833Sgshapiro ret = 0; 72638032Speter } 727244833Sgshapiro else 728244833Sgshapiro { 729244833Sgshapiro for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 730244833Sgshapiro { 731244833Sgshapiro if (QS_IS_ATTEMPTED(q->q_state)) 732244833Sgshapiro { 733244833Sgshapiro ret = 0; 734244833Sgshapiro break; 735244833Sgshapiro } 736244833Sgshapiro } 737244833Sgshapiro } 738244833Sgshapiro 739244833Sgshapiro /* restore state */ 740244833Sgshapiro sm_rpool_free(ee->e_rpool); 741244833Sgshapiro CurEnv = oldcur; 742244833Sgshapiro returndepth--; 743244833Sgshapiro 744244833Sgshapiro return ret; 74538032Speter} 746244833Sgshapiro 747363466Sgshapiro 74890795Sgshapiro/* 749363466Sgshapiro** DSNTYPENAME -- Returns the DSN name of the addrtype for this address 750363466Sgshapiro** 751363466Sgshapiro** Sendmail's addrtypes are largely in different universes, and 752363466Sgshapiro** 'fred' may be a valid address in different addrtype 753363466Sgshapiro** universes. 754363466Sgshapiro** 755363466Sgshapiro** EAI extends the rfc822 universe rather than introduce a new 756363466Sgshapiro** universe. Because of that, sendmail uses the rfc822 addrtype, 757363466Sgshapiro** but names it utf-8 when the EAI DSN extension requires that. 758363466Sgshapiro** 759363466Sgshapiro** Parameters: 760363466Sgshapiro** addrtype -- address type 761363466Sgshapiro** addr -- the address 762363466Sgshapiro** 763363466Sgshapiro** Returns: 764363466Sgshapiro** type for DSN 765363466Sgshapiro** 766363466Sgshapiro*/ 767363466Sgshapiro 768363466Sgshapirostatic const char *dsntypename __P((const char *, const char *)); 769363466Sgshapiro 770363466Sgshapirostatic const char * 771363466Sgshapirodsntypename(addrtype, addr) 772363466Sgshapiro const char *addrtype; 773363466Sgshapiro const char *addr; 774363466Sgshapiro{ 775363466Sgshapiro if (sm_strcasecmp(addrtype, "rfc822") != 0) 776363466Sgshapiro return addrtype; 777363466Sgshapiro#if _FFR_EAI 778363466Sgshapiro if (!addr_is_ascii(addr)) 779363466Sgshapiro return "utf-8"; 780363466Sgshapiro#endif 781363466Sgshapiro return "rfc822"; 782363466Sgshapiro} 783363466Sgshapiro 784363466Sgshapiro 785363466Sgshapiro/* 78638032Speter** ERRBODY -- output the body of an error message. 78738032Speter** 78838032Speter** Typically this is a copy of the transcript plus a copy of the 78938032Speter** original offending message. 79038032Speter** 79138032Speter** Parameters: 79238032Speter** mci -- the mailer connection information. 79338032Speter** e -- the envelope we are working in. 79490795Sgshapiro** separator -- any possible MIME separator (unused). 79538032Speter** 79638032Speter** Returns: 797159613Sgshapiro** true iff body was written successfully 79838032Speter** 79938032Speter** Side Effects: 80038032Speter** Outputs the body of an error message. 80138032Speter*/ 80238032Speter 80364565Sgshapiro/* ARGSUSED2 */ 804157006Sgshapirostatic bool 80538032Spetererrbody(mci, e, separator) 80638032Speter register MCI *mci; 80738032Speter register ENVELOPE *e; 80838032Speter char *separator; 80938032Speter{ 81064565Sgshapiro bool printheader; 81164565Sgshapiro bool sendbody; 81264565Sgshapiro bool pm_notify; 81364565Sgshapiro int save_errno; 81490795Sgshapiro register SM_FILE_T *xfile; 81538032Speter char *p; 81638032Speter register ADDRESS *q = NULL; 81790795Sgshapiro char actual[MAXLINE]; 81838032Speter char buf[MAXLINE]; 81938032Speter 82038032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 82138032Speter { 822157006Sgshapiro if (!putline("", mci)) 823157006Sgshapiro goto writeerr; 82438032Speter mci->mci_flags &= ~MCIF_INHEADER; 82538032Speter } 82638032Speter if (e->e_parent == NULL) 82738032Speter { 82838032Speter syserr("errbody: null parent"); 829157006Sgshapiro if (!putline(" ----- Original message lost -----\n", mci)) 830157006Sgshapiro goto writeerr; 831157006Sgshapiro return true; 83238032Speter } 83338032Speter 83438032Speter /* 83538032Speter ** Output MIME header. 83638032Speter */ 83738032Speter 83838032Speter if (e->e_msgboundary != NULL) 83938032Speter { 840168520Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, "--", e->e_msgboundary); 841157006Sgshapiro if (!putline("This is a MIME-encapsulated message", mci) || 842157006Sgshapiro !putline("", mci) || 843157006Sgshapiro !putline(buf, mci) || 844157006Sgshapiro !putline("", mci)) 845157006Sgshapiro goto writeerr; 84638032Speter } 84738032Speter 84838032Speter /* 84938032Speter ** Output introductory information. 85038032Speter */ 85138032Speter 85290795Sgshapiro pm_notify = false; 85338032Speter p = hvalue("subject", e->e_header); 85438032Speter if (p != NULL && strncmp(p, "Postmaster ", 11) == 0) 85590795Sgshapiro pm_notify = true; 85638032Speter else 85738032Speter { 85838032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 85964565Sgshapiro { 86064565Sgshapiro if (QS_IS_BADADDR(q->q_state)) 86138032Speter break; 86264565Sgshapiro } 86338032Speter } 86438032Speter if (!pm_notify && q == NULL && 86538032Speter !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags)) 86638032Speter { 867157006Sgshapiro if (!putline(" **********************************************", 868157006Sgshapiro mci) || 869157006Sgshapiro !putline(" ** THIS IS A WARNING MESSAGE ONLY **", 870157006Sgshapiro mci) || 871157006Sgshapiro !putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **", 872157006Sgshapiro mci) || 873157006Sgshapiro !putline(" **********************************************", 874157006Sgshapiro mci) || 875157006Sgshapiro !putline("", mci)) 876157006Sgshapiro goto writeerr; 87738032Speter } 878168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 87990795Sgshapiro "The original message was received at %s", 88090795Sgshapiro arpadate(ctime(&e->e_parent->e_ctime))); 881157006Sgshapiro if (!putline(buf, mci)) 882157006Sgshapiro goto writeerr; 883168520Sgshapiro expand("from \201_", buf, sizeof(buf), e->e_parent); 884157006Sgshapiro if (!putline(buf, mci)) 885157006Sgshapiro goto writeerr; 88664565Sgshapiro 88764565Sgshapiro /* include id in postmaster copies */ 88864565Sgshapiro if (pm_notify && e->e_parent->e_id != NULL) 88964565Sgshapiro { 890168520Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, "with id ", 89190795Sgshapiro e->e_parent->e_id); 892157006Sgshapiro if (!putline(buf, mci)) 893157006Sgshapiro goto writeerr; 89464565Sgshapiro } 895157006Sgshapiro if (!putline("", mci)) 896157006Sgshapiro goto writeerr; 89738032Speter 89838032Speter /* 89938032Speter ** Output error message header (if specified and available). 90038032Speter */ 90138032Speter 90264565Sgshapiro if (ErrMsgFile != NULL && 90364565Sgshapiro !bitset(EF_SENDRECEIPT, e->e_parent->e_flags)) 90438032Speter { 90538032Speter if (*ErrMsgFile == '/') 90638032Speter { 90764565Sgshapiro long sff = SFF_ROOTOK|SFF_REGONLY; 90838032Speter 90938032Speter if (DontLockReadFiles) 91038032Speter sff |= SFF_NOLOCK; 91164565Sgshapiro if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH, 91264565Sgshapiro DontBlameSendmail)) 91338032Speter sff |= SFF_SAFEDIRPATH; 91438032Speter xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff); 91538032Speter if (xfile != NULL) 91638032Speter { 91790795Sgshapiro while (sm_io_fgets(xfile, SM_TIME_DEFAULT, buf, 918249729Sgshapiro sizeof(buf)) >= 0) 91938032Speter { 920168520Sgshapiro int lbs; 921168520Sgshapiro bool putok; 922168520Sgshapiro char *lbp; 923168520Sgshapiro 924168520Sgshapiro lbs = sizeof(buf); 925168520Sgshapiro lbp = translate_dollars(buf, buf, &lbs); 926168520Sgshapiro expand(lbp, lbp, lbs, e); 927168520Sgshapiro putok = putline(lbp, mci); 928168520Sgshapiro if (lbp != buf) 929168520Sgshapiro sm_free(lbp); 930168520Sgshapiro if (!putok) 931157006Sgshapiro goto writeerr; 93238032Speter } 93390795Sgshapiro (void) sm_io_close(xfile, SM_TIME_DEFAULT); 934157006Sgshapiro if (!putline("\n", mci)) 935157006Sgshapiro goto writeerr; 93638032Speter } 93738032Speter } 93838032Speter else 93938032Speter { 940168520Sgshapiro expand(ErrMsgFile, buf, sizeof(buf), e); 941157006Sgshapiro if (!putline(buf, mci) || !putline("", mci)) 942157006Sgshapiro goto writeerr; 94338032Speter } 94438032Speter } 94538032Speter 94638032Speter /* 94738032Speter ** Output message introduction 94838032Speter */ 94938032Speter 95090795Sgshapiro /* permanent fatal errors */ 95190795Sgshapiro printheader = true; 95238032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 95338032Speter { 95464565Sgshapiro if (!QS_IS_BADADDR(q->q_state) || 95538032Speter !bitset(QPINGONFAILURE, q->q_flags)) 95638032Speter continue; 95738032Speter 95838032Speter if (printheader) 95938032Speter { 960157006Sgshapiro if (!putline(" ----- The following addresses had permanent fatal errors -----", 961157006Sgshapiro mci)) 962157006Sgshapiro goto writeerr; 96390795Sgshapiro printheader = false; 96438032Speter } 96538032Speter 96690795Sgshapiro (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR), 967168520Sgshapiro sizeof(buf)); 968157006Sgshapiro if (!putline(buf, mci)) 969157006Sgshapiro goto writeerr; 97064565Sgshapiro if (q->q_rstatus != NULL) 97164565Sgshapiro { 972168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 97390795Sgshapiro " (reason: %s)", 97490795Sgshapiro shortenstring(exitstat(q->q_rstatus), 97590795Sgshapiro MAXSHORTSTR)); 976157006Sgshapiro if (!putline(buf, mci)) 977157006Sgshapiro goto writeerr; 97864565Sgshapiro } 97938032Speter if (q->q_alias != NULL) 98038032Speter { 981168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 98290795Sgshapiro " (expanded from: %s)", 98390795Sgshapiro shortenstring(q->q_alias->q_paddr, 98490795Sgshapiro MAXSHORTSTR)); 985157006Sgshapiro if (!putline(buf, mci)) 986157006Sgshapiro goto writeerr; 98738032Speter } 98838032Speter } 989157006Sgshapiro if (!printheader && !putline("", mci)) 990157006Sgshapiro goto writeerr; 99138032Speter 99290795Sgshapiro /* transient non-fatal errors */ 99390795Sgshapiro printheader = true; 99438032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 99538032Speter { 99664565Sgshapiro if (QS_IS_BADADDR(q->q_state) || 99738032Speter !bitset(QPRIMARY, q->q_flags) || 99890795Sgshapiro !bitset(QBYNDELAY, q->q_flags) || 99938032Speter !bitset(QDELAYED, q->q_flags)) 100038032Speter continue; 100138032Speter 100238032Speter if (printheader) 100338032Speter { 1004157006Sgshapiro if (!putline(" ----- The following addresses had transient non-fatal errors -----", 1005157006Sgshapiro mci)) 1006157006Sgshapiro goto writeerr; 100790795Sgshapiro printheader = false; 100838032Speter } 100938032Speter 101090795Sgshapiro (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR), 1011168520Sgshapiro sizeof(buf)); 1012157006Sgshapiro if (!putline(buf, mci)) 1013157006Sgshapiro goto writeerr; 101438032Speter if (q->q_alias != NULL) 101538032Speter { 1016168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 101790795Sgshapiro " (expanded from: %s)", 101890795Sgshapiro shortenstring(q->q_alias->q_paddr, 101990795Sgshapiro MAXSHORTSTR)); 1020157006Sgshapiro if (!putline(buf, mci)) 1021157006Sgshapiro goto writeerr; 102238032Speter } 102338032Speter } 1024157006Sgshapiro if (!printheader && !putline("", mci)) 1025157006Sgshapiro goto writeerr; 102638032Speter 102790795Sgshapiro /* successful delivery notifications */ 102890795Sgshapiro printheader = true; 102938032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 103038032Speter { 103164565Sgshapiro if (QS_IS_BADADDR(q->q_state) || 103238032Speter !bitset(QPRIMARY, q->q_flags) || 103390795Sgshapiro bitset(QBYNDELAY, q->q_flags) || 103438032Speter bitset(QDELAYED, q->q_flags)) 103538032Speter continue; 103690795Sgshapiro else if (bitset(QBYNRELAY, q->q_flags)) 103790795Sgshapiro p = "Deliver-By notify: relayed"; 103890795Sgshapiro else if (bitset(QBYTRACE, q->q_flags)) 103990795Sgshapiro p = "Deliver-By trace: relayed"; 104038032Speter else if (!bitset(QPINGONSUCCESS, q->q_flags)) 104138032Speter continue; 104238032Speter else if (bitset(QRELAYED, q->q_flags)) 104338032Speter p = "relayed to non-DSN-aware mailer"; 104438032Speter else if (bitset(QDELIVERED, q->q_flags)) 104538032Speter { 104638032Speter if (bitset(QEXPANDED, q->q_flags)) 104738032Speter p = "successfully delivered to mailing list"; 104838032Speter else 104938032Speter p = "successfully delivered to mailbox"; 105038032Speter } 105138032Speter else if (bitset(QEXPANDED, q->q_flags)) 105238032Speter p = "expanded by alias"; 105338032Speter else 105438032Speter continue; 105538032Speter 105638032Speter if (printheader) 105738032Speter { 1058157006Sgshapiro if (!putline(" ----- The following addresses had successful delivery notifications -----", 1059157006Sgshapiro mci)) 1060157006Sgshapiro goto writeerr; 106190795Sgshapiro printheader = false; 106238032Speter } 106338032Speter 1064168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", 106564565Sgshapiro shortenstring(q->q_paddr, MAXSHORTSTR), p); 1066157006Sgshapiro if (!putline(buf, mci)) 1067157006Sgshapiro goto writeerr; 106838032Speter if (q->q_alias != NULL) 106938032Speter { 1070168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 107190795Sgshapiro " (expanded from: %s)", 107290795Sgshapiro shortenstring(q->q_alias->q_paddr, 107390795Sgshapiro MAXSHORTSTR)); 1074157006Sgshapiro if (!putline(buf, mci)) 1075157006Sgshapiro goto writeerr; 107638032Speter } 107738032Speter } 1078157006Sgshapiro if (!printheader && !putline("", mci)) 1079157006Sgshapiro goto writeerr; 108038032Speter 108138032Speter /* 108238032Speter ** Output transcript of errors 108338032Speter */ 108438032Speter 108590795Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 108664565Sgshapiro if (e->e_parent->e_xfp == NULL) 108738032Speter { 1088157006Sgshapiro if (!putline(" ----- Transcript of session is unavailable -----\n", 1089157006Sgshapiro mci)) 1090157006Sgshapiro goto writeerr; 109138032Speter } 109238032Speter else 109338032Speter { 1094249729Sgshapiro int blen; 1095249729Sgshapiro 109690795Sgshapiro printheader = true; 109764565Sgshapiro (void) bfrewind(e->e_parent->e_xfp); 109838032Speter if (e->e_xfp != NULL) 109990795Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 1100249729Sgshapiro while ((blen = sm_io_fgets(e->e_parent->e_xfp, SM_TIME_DEFAULT, 1101249729Sgshapiro buf, sizeof(buf))) >= 0) 110238032Speter { 1103157006Sgshapiro if (printheader && !putline(" ----- Transcript of session follows -----\n", 1104157006Sgshapiro mci)) 1105157006Sgshapiro goto writeerr; 110690795Sgshapiro printheader = false; 1107249729Sgshapiro if (!putxline(buf, blen, mci, PXLF_MAPFROM)) 1108157006Sgshapiro goto writeerr; 110938032Speter } 111038032Speter } 111138032Speter errno = 0; 111238032Speter 111338032Speter#if DSN 111438032Speter /* 111538032Speter ** Output machine-readable version. 111638032Speter */ 111738032Speter 111838032Speter if (e->e_msgboundary != NULL) 111938032Speter { 1120168520Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, "--", e->e_msgboundary); 1121157006Sgshapiro if (!putline("", mci) || 1122157006Sgshapiro !putline(buf, mci) || 1123363466Sgshapiro#if _FFR_EAI 1124363466Sgshapiro !putline(e->e_parent->e_smtputf8 1125363466Sgshapiro ? "Content-Type: message/global-delivery-status" 1126363466Sgshapiro : "Content-Type: message/delivery-status", mci) || 1127363466Sgshapiro#else 1128157006Sgshapiro !putline("Content-Type: message/delivery-status", mci) || 1129363466Sgshapiro#endif 1130157006Sgshapiro !putline("", mci)) 1131157006Sgshapiro goto writeerr; 113238032Speter 113338032Speter /* 113438032Speter ** Output per-message information. 113538032Speter */ 113638032Speter 113738032Speter /* original envelope id from MAIL FROM: line */ 113838032Speter if (e->e_parent->e_envid != NULL) 113938032Speter { 1140168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 114164565Sgshapiro "Original-Envelope-Id: %.800s", 114264565Sgshapiro xuntextify(e->e_parent->e_envid)); 1143157006Sgshapiro if (!putline(buf, mci)) 1144157006Sgshapiro goto writeerr; 114538032Speter } 114638032Speter 114738032Speter /* Reporting-MTA: is us (required) */ 1148168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 114990795Sgshapiro "Reporting-MTA: dns; %.800s", MyHostName); 1150157006Sgshapiro if (!putline(buf, mci)) 1151157006Sgshapiro goto writeerr; 115238032Speter 115338032Speter /* DSN-Gateway: not relevant since we are not translating */ 115438032Speter 115538032Speter /* Received-From-MTA: shows where we got this message from */ 115638032Speter if (RealHostName != NULL) 115738032Speter { 115838032Speter /* XXX use $s for type? */ 115938032Speter if (e->e_parent->e_from.q_mailer == NULL || 116038032Speter (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL) 116138032Speter p = "dns"; 1162168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 116364565Sgshapiro "Received-From-MTA: %s; %.800s", 116464565Sgshapiro p, RealHostName); 1165157006Sgshapiro if (!putline(buf, mci)) 1166157006Sgshapiro goto writeerr; 116738032Speter } 116838032Speter 116938032Speter /* Arrival-Date: -- when it arrived here */ 1170168520Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, "Arrival-Date: ", 117164565Sgshapiro arpadate(ctime(&e->e_parent->e_ctime))); 1172157006Sgshapiro if (!putline(buf, mci)) 1173157006Sgshapiro goto writeerr; 117438032Speter 117590795Sgshapiro /* Deliver-By-Date: -- when it should have been delivered */ 117690795Sgshapiro if (IS_DLVR_BY(e->e_parent)) 117790795Sgshapiro { 117890795Sgshapiro time_t dbyd; 117990795Sgshapiro 118090795Sgshapiro dbyd = e->e_parent->e_ctime + e->e_parent->e_deliver_by; 1181168520Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, 118290795Sgshapiro "Deliver-By-Date: ", 118390795Sgshapiro arpadate(ctime(&dbyd))); 1184157006Sgshapiro if (!putline(buf, mci)) 1185157006Sgshapiro goto writeerr; 118690795Sgshapiro } 118790795Sgshapiro 118838032Speter /* 118938032Speter ** Output per-address information. 119038032Speter */ 119138032Speter 119238032Speter for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 119338032Speter { 119438032Speter char *action; 119538032Speter 119664565Sgshapiro if (QS_IS_BADADDR(q->q_state)) 119771348Sgshapiro { 119871348Sgshapiro /* RFC 1891, 6.2.6 (b) */ 119971348Sgshapiro if (bitset(QHASNOTIFY, q->q_flags) && 120071348Sgshapiro !bitset(QPINGONFAILURE, q->q_flags)) 120171348Sgshapiro continue; 120238032Speter action = "failed"; 120371348Sgshapiro } 120438032Speter else if (!bitset(QPRIMARY, q->q_flags)) 120538032Speter continue; 120638032Speter else if (bitset(QDELIVERED, q->q_flags)) 120738032Speter { 120838032Speter if (bitset(QEXPANDED, q->q_flags)) 120938032Speter action = "delivered (to mailing list)"; 121038032Speter else 121138032Speter action = "delivered (to mailbox)"; 121238032Speter } 121338032Speter else if (bitset(QRELAYED, q->q_flags)) 121438032Speter action = "relayed (to non-DSN-aware mailer)"; 121538032Speter else if (bitset(QEXPANDED, q->q_flags)) 121638032Speter action = "expanded (to multi-recipient alias)"; 121738032Speter else if (bitset(QDELAYED, q->q_flags)) 121838032Speter action = "delayed"; 121990795Sgshapiro else if (bitset(QBYTRACE, q->q_flags)) 122090795Sgshapiro action = "relayed (Deliver-By trace mode)"; 122190795Sgshapiro else if (bitset(QBYNDELAY, q->q_flags)) 122290795Sgshapiro action = "delayed (Deliver-By notify mode)"; 122390795Sgshapiro else if (bitset(QBYNRELAY, q->q_flags)) 122490795Sgshapiro action = "relayed (Deliver-By notify mode)"; 122538032Speter else 122638032Speter continue; 122738032Speter 1228157006Sgshapiro if (!putline("", mci)) 1229157006Sgshapiro goto writeerr; 123038032Speter 123138032Speter /* Original-Recipient: -- passed from on high */ 123238032Speter if (q->q_orcpt != NULL) 123338032Speter { 1234249729Sgshapiro p = strchr(q->q_orcpt, ';'); 1235249729Sgshapiro 1236249729Sgshapiro /* 1237249729Sgshapiro ** p == NULL shouldn't happen due to 1238249729Sgshapiro ** check in srvrsmtp.c 1239249729Sgshapiro ** we could log an error in this case. 1240249729Sgshapiro */ 1241249729Sgshapiro 1242249729Sgshapiro if (p != NULL) 1243249729Sgshapiro { 1244249729Sgshapiro *p = '\0'; 1245249729Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 1246249729Sgshapiro "Original-Recipient: %.100s;%.700s", 1247249729Sgshapiro q->q_orcpt, xuntextify(p + 1)); 1248249729Sgshapiro *p = ';'; 1249249729Sgshapiro if (!putline(buf, mci)) 1250249729Sgshapiro goto writeerr; 1251249729Sgshapiro } 125238032Speter } 125338032Speter 125490795Sgshapiro /* Figure out actual recipient */ 125590795Sgshapiro actual[0] = '\0'; 125690795Sgshapiro if (q->q_user[0] != '\0') 125738032Speter { 125864565Sgshapiro if (q->q_mailer != NULL && 125964565Sgshapiro q->q_mailer->m_addrtype != NULL) 126064565Sgshapiro p = q->q_mailer->m_addrtype; 126164565Sgshapiro else 126264565Sgshapiro p = "rfc822"; 126364565Sgshapiro 126490795Sgshapiro if (sm_strcasecmp(p, "rfc822") == 0 && 126564565Sgshapiro strchr(q->q_user, '@') == NULL) 126638032Speter { 126790795Sgshapiro (void) sm_snprintf(actual, 1268168520Sgshapiro sizeof(actual), 126990795Sgshapiro "%s; %.700s@%.100s", 1270363466Sgshapiro dsntypename(p, q->q_user), 1271363466Sgshapiro q->q_user, 127290795Sgshapiro MyHostName); 127338032Speter } 127438032Speter else 127538032Speter { 127690795Sgshapiro (void) sm_snprintf(actual, 1277168520Sgshapiro sizeof(actual), 127890795Sgshapiro "%s; %.800s", 1279363466Sgshapiro dsntypename(p, q->q_user), 1280363466Sgshapiro q->q_user); 128138032Speter } 128290795Sgshapiro } 128390795Sgshapiro 128490795Sgshapiro /* Final-Recipient: -- the name from the RCPT command */ 128590795Sgshapiro if (q->q_finalrcpt == NULL) 128690795Sgshapiro { 128790795Sgshapiro /* should never happen */ 128890795Sgshapiro sm_syslog(LOG_ERR, e->e_id, 128990795Sgshapiro "returntosender: q_finalrcpt is NULL"); 129090795Sgshapiro 129190795Sgshapiro /* try to fall back to the actual recipient */ 129290795Sgshapiro if (actual[0] != '\0') 129390795Sgshapiro q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool, 129490795Sgshapiro actual); 129590795Sgshapiro } 129690795Sgshapiro 1297363466Sgshapiro#if _FFR_EAI 1298363466Sgshapiro if (sm_strncasecmp("rfc822;", q->q_finalrcpt, 7) == 0 && 1299363466Sgshapiro !addr_is_ascii(q->q_user)) 1300363466Sgshapiro { 1301363466Sgshapiro char *a; 1302363466Sgshapiro char utf8rcpt[1024]; 1303363466Sgshapiro 1304363466Sgshapiro a = strchr(q->q_finalrcpt, ';'); 1305363466Sgshapiro while(*a == ';' || *a == ' ') 1306363466Sgshapiro a++; 1307363466Sgshapiro sm_snprintf(utf8rcpt, sizeof(utf8rcpt), 1308363466Sgshapiro "utf-8; %.800s", a); 1309363466Sgshapiro q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool, 1310363466Sgshapiro utf8rcpt); 1311363466Sgshapiro } 1312363466Sgshapiro#endif 1313363466Sgshapiro 131490795Sgshapiro if (q->q_finalrcpt != NULL) 131590795Sgshapiro { 1316168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 131790795Sgshapiro "Final-Recipient: %s", 131890795Sgshapiro q->q_finalrcpt); 1319157006Sgshapiro if (!putline(buf, mci)) 1320157006Sgshapiro goto writeerr; 132138032Speter } 132238032Speter 132390795Sgshapiro /* X-Actual-Recipient: -- the real problem address */ 132490795Sgshapiro if (actual[0] != '\0' && 132590795Sgshapiro q->q_finalrcpt != NULL && 1326141862Sgshapiro !bitset(PRIV_NOACTUALRECIPIENT, PrivacyFlags) && 132790795Sgshapiro strcmp(actual, q->q_finalrcpt) != 0) 132890795Sgshapiro { 1329168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 133090795Sgshapiro "X-Actual-Recipient: %s", 133190795Sgshapiro actual); 1332157006Sgshapiro if (!putline(buf, mci)) 1333157006Sgshapiro goto writeerr; 133490795Sgshapiro } 133590795Sgshapiro 133638032Speter /* Action: -- what happened? */ 1337168520Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, "Action: ", 133890795Sgshapiro action); 1339157006Sgshapiro if (!putline(buf, mci)) 1340157006Sgshapiro goto writeerr; 134138032Speter 134238032Speter /* Status: -- what _really_ happened? */ 134338032Speter if (q->q_status != NULL) 134438032Speter p = q->q_status; 134564565Sgshapiro else if (QS_IS_BADADDR(q->q_state)) 134638032Speter p = "5.0.0"; 134764565Sgshapiro else if (QS_IS_QUEUEUP(q->q_state)) 134838032Speter p = "4.0.0"; 134938032Speter else 135038032Speter p = "2.0.0"; 1351168520Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, "Status: ", p); 1352157006Sgshapiro if (!putline(buf, mci)) 1353157006Sgshapiro goto writeerr; 135438032Speter 135538032Speter /* Remote-MTA: -- who was I talking to? */ 135638032Speter if (q->q_statmta != NULL) 135738032Speter { 135838032Speter if (q->q_mailer == NULL || 135938032Speter (p = q->q_mailer->m_mtatype) == NULL) 136038032Speter p = "dns"; 1361168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 136264565Sgshapiro "Remote-MTA: %s; %.800s", 136364565Sgshapiro p, q->q_statmta); 136438032Speter p = &buf[strlen(buf) - 1]; 136538032Speter if (*p == '.') 136638032Speter *p = '\0'; 1367157006Sgshapiro if (!putline(buf, mci)) 1368157006Sgshapiro goto writeerr; 136938032Speter } 137038032Speter 137138032Speter /* Diagnostic-Code: -- actual result from other end */ 137238032Speter if (q->q_rstatus != NULL) 137338032Speter { 1374159613Sgshapiro if (q->q_mailer == NULL || 1375159613Sgshapiro (p = q->q_mailer->m_diagtype) == NULL) 137638032Speter p = "smtp"; 1377168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 137864565Sgshapiro "Diagnostic-Code: %s; %.800s", 137964565Sgshapiro p, q->q_rstatus); 1380157006Sgshapiro if (!putline(buf, mci)) 1381157006Sgshapiro goto writeerr; 138238032Speter } 138338032Speter 138438032Speter /* Last-Attempt-Date: -- fine granularity */ 138538032Speter if (q->q_statdate == (time_t) 0L) 138690795Sgshapiro q->q_statdate = curtime(); 1387168520Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, 138890795Sgshapiro "Last-Attempt-Date: ", 138964565Sgshapiro arpadate(ctime(&q->q_statdate))); 1390157006Sgshapiro if (!putline(buf, mci)) 1391157006Sgshapiro goto writeerr; 139238032Speter 139338032Speter /* Will-Retry-Until: -- for delayed messages only */ 139464565Sgshapiro if (QS_IS_QUEUEUP(q->q_state)) 139538032Speter { 139638032Speter time_t xdate; 139738032Speter 139838032Speter xdate = e->e_parent->e_ctime + 139938032Speter TimeOuts.to_q_return[e->e_parent->e_timeoutclass]; 1400168520Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, 140190795Sgshapiro "Will-Retry-Until: ", 140264565Sgshapiro arpadate(ctime(&xdate))); 1403157006Sgshapiro if (!putline(buf, mci)) 1404157006Sgshapiro goto writeerr; 140538032Speter } 140638032Speter } 140738032Speter } 140864565Sgshapiro#endif /* DSN */ 140938032Speter 141038032Speter /* 141138032Speter ** Output text of original message 141238032Speter */ 141338032Speter 1414157006Sgshapiro if (!putline("", mci)) 1415157006Sgshapiro goto writeerr; 141638032Speter if (bitset(EF_HAS_DF, e->e_parent->e_flags)) 141738032Speter { 141838032Speter sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) && 141938032Speter !bitset(EF_NO_BODY_RETN, e->e_flags); 142038032Speter 142138032Speter if (e->e_msgboundary == NULL) 142238032Speter { 1423157006Sgshapiro if (!putline( 1424157006Sgshapiro sendbody 1425157006Sgshapiro ? " ----- Original message follows -----\n" 1426157006Sgshapiro : " ----- Message header follows -----\n", 1427157006Sgshapiro mci)) 1428157006Sgshapiro { 1429157006Sgshapiro goto writeerr; 1430157006Sgshapiro } 143138032Speter } 143238032Speter else 143338032Speter { 1434168520Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, "--", 143564565Sgshapiro e->e_msgboundary); 143638032Speter 1437157006Sgshapiro if (!putline(buf, mci)) 1438157006Sgshapiro goto writeerr; 1439363466Sgshapiro#if _FFR_EAI 1440363466Sgshapiro if (e->e_parent->e_smtputf8) 1441363466Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, 1442363466Sgshapiro "Content-Type: message/global", 1443363466Sgshapiro sendbody ? "" : "-headers"); 1444363466Sgshapiro else 1445363466Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, 1446363466Sgshapiro "Content-Type: ", 1447363466Sgshapiro sendbody ? "message/rfc822" 1448363466Sgshapiro : "text/rfc822-headers"); 1449363466Sgshapiro#else /* _FFR_EAI */ 1450168520Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, "Content-Type: ", 145164565Sgshapiro sendbody ? "message/rfc822" 145264565Sgshapiro : "text/rfc822-headers"); 1453363466Sgshapiro#endif /* _FFR_EAI */ 1454157006Sgshapiro if (!putline(buf, mci)) 1455157006Sgshapiro goto writeerr; 145638032Speter 145764565Sgshapiro p = hvalue("Content-Transfer-Encoding", 145864565Sgshapiro e->e_parent->e_header); 145990795Sgshapiro if (p != NULL && sm_strcasecmp(p, "binary") != 0) 146038032Speter p = NULL; 146164565Sgshapiro if (p == NULL && 146264565Sgshapiro bitset(EF_HAS8BIT, e->e_parent->e_flags)) 146338032Speter p = "8bit"; 146438032Speter if (p != NULL) 146538032Speter { 1466168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 146764565Sgshapiro "Content-Transfer-Encoding: %s", 146864565Sgshapiro p); 1469157006Sgshapiro if (!putline(buf, mci)) 1470157006Sgshapiro goto writeerr; 147138032Speter } 147238032Speter } 1473157006Sgshapiro if (!putline("", mci)) 1474157006Sgshapiro goto writeerr; 147564565Sgshapiro save_errno = errno; 1476157006Sgshapiro if (!putheader(mci, e->e_parent->e_header, e->e_parent, 1477157006Sgshapiro M87F_OUTER)) 1478157006Sgshapiro goto writeerr; 147964565Sgshapiro errno = save_errno; 148038032Speter if (sendbody) 1481157006Sgshapiro { 1482157006Sgshapiro if (!putbody(mci, e->e_parent, e->e_msgboundary)) 1483157006Sgshapiro goto writeerr; 1484157006Sgshapiro } 148538032Speter else if (e->e_msgboundary == NULL) 148638032Speter { 1487157006Sgshapiro if (!putline("", mci) || 1488157006Sgshapiro !putline(" ----- Message body suppressed -----", 1489157006Sgshapiro mci)) 1490157006Sgshapiro { 1491157006Sgshapiro goto writeerr; 1492157006Sgshapiro } 149338032Speter } 149438032Speter } 149538032Speter else if (e->e_msgboundary == NULL) 149638032Speter { 1497157006Sgshapiro if (!putline(" ----- No message was collected -----\n", mci)) 1498157006Sgshapiro goto writeerr; 149938032Speter } 150038032Speter 150138032Speter if (e->e_msgboundary != NULL) 150238032Speter { 1503168520Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 3, "--", e->e_msgboundary, 150490795Sgshapiro "--"); 1505157006Sgshapiro if (!putline("", mci) || !putline(buf, mci)) 1506157006Sgshapiro goto writeerr; 150738032Speter } 1508157006Sgshapiro if (!putline("", mci) || 1509157006Sgshapiro sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF) 1510157006Sgshapiro goto writeerr; 151138032Speter 151238032Speter /* 151338032Speter ** Cleanup and exit 151438032Speter */ 151538032Speter 151638032Speter if (errno != 0) 1517157006Sgshapiro { 1518157006Sgshapiro writeerr: 151938032Speter syserr("errbody: I/O error"); 1520157006Sgshapiro return false; 1521157006Sgshapiro } 1522157006Sgshapiro return true; 152338032Speter} 1524157006Sgshapiro 152590795Sgshapiro/* 152638032Speter** SMTPTODSN -- convert SMTP to DSN status code 152738032Speter** 152838032Speter** Parameters: 152938032Speter** smtpstat -- the smtp status code (e.g., 550). 153038032Speter** 153138032Speter** Returns: 153238032Speter** The DSN version of the status code. 153390795Sgshapiro** 153490795Sgshapiro** Storage Management: 153590795Sgshapiro** smtptodsn() returns a pointer to a character string literal, 153690795Sgshapiro** which will remain valid forever, and thus does not need to 153790795Sgshapiro** be copied. Current code relies on this property. 153838032Speter*/ 153938032Speter 154038032Speterchar * 154138032Spetersmtptodsn(smtpstat) 154238032Speter int smtpstat; 154338032Speter{ 154438032Speter if (smtpstat < 0) 154538032Speter return "4.4.2"; 154638032Speter 154738032Speter switch (smtpstat) 154838032Speter { 154938032Speter case 450: /* Req mail action not taken: mailbox unavailable */ 155038032Speter return "4.2.0"; 155138032Speter 155238032Speter case 451: /* Req action aborted: local error in processing */ 155338032Speter return "4.3.0"; 155438032Speter 155538032Speter case 452: /* Req action not taken: insufficient sys storage */ 155638032Speter return "4.3.1"; 155738032Speter 155838032Speter case 500: /* Syntax error, command unrecognized */ 155938032Speter return "5.5.2"; 156038032Speter 156138032Speter case 501: /* Syntax error in parameters or arguments */ 156238032Speter return "5.5.4"; 156338032Speter 156438032Speter case 502: /* Command not implemented */ 156538032Speter return "5.5.1"; 156638032Speter 156738032Speter case 503: /* Bad sequence of commands */ 156838032Speter return "5.5.1"; 156938032Speter 157038032Speter case 504: /* Command parameter not implemented */ 157138032Speter return "5.5.4"; 157238032Speter 157338032Speter case 550: /* Req mail action not taken: mailbox unavailable */ 157438032Speter return "5.2.0"; 157538032Speter 157638032Speter case 551: /* User not local; please try <...> */ 157738032Speter return "5.1.6"; 157838032Speter 157938032Speter case 552: /* Req mail action aborted: exceeded storage alloc */ 158038032Speter return "5.2.2"; 158138032Speter 158238032Speter case 553: /* Req action not taken: mailbox name not allowed */ 158338032Speter return "5.1.0"; 158438032Speter 158538032Speter case 554: /* Transaction failed */ 158638032Speter return "5.0.0"; 158738032Speter } 158838032Speter 1589157006Sgshapiro if (REPLYTYPE(smtpstat) == 2) 159038032Speter return "2.0.0"; 1591157006Sgshapiro if (REPLYTYPE(smtpstat) == 4) 159238032Speter return "4.0.0"; 159338032Speter return "5.0.0"; 159438032Speter} 159590795Sgshapiro/* 159638032Speter** XTEXTIFY -- take regular text and turn it into DSN-style xtext 159738032Speter** 159838032Speter** Parameters: 159938032Speter** t -- the text to convert. 160038032Speter** taboo -- additional characters that must be encoded. 160138032Speter** 160238032Speter** Returns: 160338032Speter** The xtext-ified version of the same string. 160438032Speter*/ 160538032Speter 160638032Speterchar * 160738032Speterxtextify(t, taboo) 160838032Speter register char *t; 160938032Speter char *taboo; 161038032Speter{ 161138032Speter register char *p; 161238032Speter int l; 161338032Speter int nbogus; 161438032Speter static char *bp = NULL; 161538032Speter static int bplen = 0; 161638032Speter 161738032Speter if (taboo == NULL) 161838032Speter taboo = ""; 161938032Speter 162038032Speter /* figure out how long this xtext will have to be */ 162138032Speter nbogus = l = 0; 162238032Speter for (p = t; *p != '\0'; p++) 162338032Speter { 162438032Speter register int c = (*p & 0xff); 162538032Speter 162638032Speter /* ASCII dependence here -- this is the way the spec words it */ 162738032Speter if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || 162838032Speter strchr(taboo, c) != NULL) 162938032Speter nbogus++; 163038032Speter l++; 163138032Speter } 163290795Sgshapiro if (nbogus < 0) 163390795Sgshapiro { 163490795Sgshapiro /* since nbogus is ssize_t and wrapped, 2 * size_t would wrap */ 163590795Sgshapiro syserr("!xtextify string too long"); 163690795Sgshapiro } 163738032Speter if (nbogus == 0) 163838032Speter return t; 163938032Speter l += nbogus * 2 + 1; 164038032Speter 164138032Speter /* now allocate space if necessary for the new string */ 164238032Speter if (l > bplen) 164338032Speter { 164438032Speter if (bp != NULL) 164590795Sgshapiro sm_free(bp); /* XXX */ 164690795Sgshapiro bp = sm_pmalloc_x(l); 164738032Speter bplen = l; 164838032Speter } 164938032Speter 165038032Speter /* ok, copy the text with byte expansion */ 165138032Speter for (p = bp; *t != '\0'; ) 165238032Speter { 165338032Speter register int c = (*t++ & 0xff); 165438032Speter 165538032Speter /* ASCII dependence here -- this is the way the spec words it */ 165638032Speter if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || 165738032Speter strchr(taboo, c) != NULL) 165838032Speter { 165938032Speter *p++ = '+'; 166064565Sgshapiro *p++ = "0123456789ABCDEF"[c >> 4]; 166164565Sgshapiro *p++ = "0123456789ABCDEF"[c & 0xf]; 166238032Speter } 166338032Speter else 166438032Speter *p++ = c; 166538032Speter } 166638032Speter *p = '\0'; 166738032Speter return bp; 166838032Speter} 166990795Sgshapiro/* 167038032Speter** XUNTEXTIFY -- take xtext and turn it into plain text 167138032Speter** 167238032Speter** Parameters: 167338032Speter** t -- the xtextified text. 167438032Speter** 167538032Speter** Returns: 167638032Speter** The decoded text. No attempt is made to deal with 167738032Speter** null strings in the resulting text. 167838032Speter*/ 167938032Speter 168038032Speterchar * 168138032Speterxuntextify(t) 168238032Speter register char *t; 168338032Speter{ 168438032Speter register char *p; 168538032Speter int l; 168638032Speter static char *bp = NULL; 168738032Speter static int bplen = 0; 168838032Speter 168938032Speter /* heuristic -- if no plus sign, just return the input */ 169038032Speter if (strchr(t, '+') == NULL) 169138032Speter return t; 169238032Speter 169338032Speter /* xtext is always longer than decoded text */ 169438032Speter l = strlen(t); 169538032Speter if (l > bplen) 169638032Speter { 169738032Speter if (bp != NULL) 169890795Sgshapiro sm_free(bp); /* XXX */ 169938032Speter bp = xalloc(l); 170038032Speter bplen = l; 170138032Speter } 170238032Speter 170338032Speter /* ok, copy the text with byte compression */ 170438032Speter for (p = bp; *t != '\0'; t++) 170538032Speter { 170638032Speter register int c = *t & 0xff; 170738032Speter 170838032Speter if (c != '+') 170938032Speter { 171038032Speter *p++ = c; 171138032Speter continue; 171238032Speter } 171338032Speter 171438032Speter c = *++t & 0xff; 171538032Speter if (!isascii(c) || !isxdigit(c)) 171638032Speter { 171738032Speter /* error -- first digit is not hex */ 171838032Speter usrerr("bogus xtext: +%c", c); 171938032Speter t--; 172038032Speter continue; 172138032Speter } 172238032Speter if (isdigit(c)) 172338032Speter c -= '0'; 172438032Speter else if (isupper(c)) 172538032Speter c -= 'A' - 10; 172638032Speter else 172738032Speter c -= 'a' - 10; 172838032Speter *p = c << 4; 172938032Speter 173038032Speter c = *++t & 0xff; 173138032Speter if (!isascii(c) || !isxdigit(c)) 173238032Speter { 173338032Speter /* error -- second digit is not hex */ 173438032Speter usrerr("bogus xtext: +%x%c", *p >> 4, c); 173538032Speter t--; 173638032Speter continue; 173738032Speter } 173838032Speter if (isdigit(c)) 173938032Speter c -= '0'; 174038032Speter else if (isupper(c)) 174138032Speter c -= 'A' - 10; 174238032Speter else 174338032Speter c -= 'a' - 10; 174438032Speter *p++ |= c; 174538032Speter } 174638032Speter *p = '\0'; 174738032Speter return bp; 174838032Speter} 174990795Sgshapiro/* 175038032Speter** XTEXTOK -- check if a string is legal xtext 175138032Speter** 175238032Speter** Xtext is used in Delivery Status Notifications. The spec was 175338032Speter** taken from RFC 1891, ``SMTP Service Extension for Delivery 175438032Speter** Status Notifications''. 175538032Speter** 175638032Speter** Parameters: 175738032Speter** s -- the string to check. 175838032Speter** 175938032Speter** Returns: 176090795Sgshapiro** true -- if 's' is legal xtext. 176190795Sgshapiro** false -- if it has any illegal characters in it. 176238032Speter*/ 176338032Speter 176438032Speterbool 176538032Speterxtextok(s) 176638032Speter char *s; 176738032Speter{ 176838032Speter int c; 176938032Speter 177038032Speter while ((c = *s++) != '\0') 177138032Speter { 177238032Speter if (c == '+') 177338032Speter { 177438032Speter c = *s++; 177538032Speter if (!isascii(c) || !isxdigit(c)) 177690795Sgshapiro return false; 177738032Speter c = *s++; 177838032Speter if (!isascii(c) || !isxdigit(c)) 177990795Sgshapiro return false; 178038032Speter } 178138032Speter else if (c < '!' || c > '~' || c == '=') 178290795Sgshapiro return false; 178338032Speter } 178490795Sgshapiro return true; 178538032Speter} 1786249729Sgshapiro 178790795Sgshapiro/* 1788249729Sgshapiro** ISATOM -- check if a string is an "atom" 1789249729Sgshapiro** 1790249729Sgshapiro** Parameters: 1791249729Sgshapiro** s -- the string to check. 1792249729Sgshapiro** 1793249729Sgshapiro** Returns: 1794249729Sgshapiro** true -- iff s is an atom 1795249729Sgshapiro*/ 1796249729Sgshapiro 1797249729Sgshapirobool 1798249729Sgshapiroisatom(s) 1799249729Sgshapiro const char *s; 1800249729Sgshapiro{ 1801249729Sgshapiro int c; 1802249729Sgshapiro 1803249729Sgshapiro if (s == NULL || *s == '\0') 1804249729Sgshapiro return false; 1805249729Sgshapiro while ((c = *s++) != '\0') 1806249729Sgshapiro { 1807249729Sgshapiro if (strchr("()<>@,;:\\.[]\"", c) != NULL) 1808249729Sgshapiro return false; 1809249729Sgshapiro if (c < '!' || c > '~') 1810249729Sgshapiro return false; 1811249729Sgshapiro } 1812249729Sgshapiro return true; 1813249729Sgshapiro} 1814249729Sgshapiro/* 181538032Speter** PRUNEROUTE -- prune an RFC-822 source route 181664565Sgshapiro** 181738032Speter** Trims down a source route to the last internet-registered hop. 181838032Speter** This is encouraged by RFC 1123 section 5.3.3. 181964565Sgshapiro** 182038032Speter** Parameters: 182138032Speter** addr -- the address 182264565Sgshapiro** 182338032Speter** Returns: 182490795Sgshapiro** true -- address was modified 182590795Sgshapiro** false -- address could not be pruned 182664565Sgshapiro** 182738032Speter** Side Effects: 182838032Speter** modifies addr in-place 182938032Speter*/ 183038032Speter 183164565Sgshapirostatic bool 183238032Speterpruneroute(addr) 183338032Speter char *addr; 183438032Speter{ 183538032Speter#if NAMED_BIND 183638032Speter char *start, *at, *comma; 183738032Speter char c; 183890795Sgshapiro int braclev; 183938032Speter int rcode; 184038032Speter int i; 184138032Speter char hostbuf[BUFSIZ]; 184238032Speter char *mxhosts[MAXMXHOSTS + 1]; 184338032Speter 184438032Speter /* check to see if this is really a route-addr */ 184538032Speter if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') 184690795Sgshapiro return false; 184790795Sgshapiro 184890795Sgshapiro /* 184990795Sgshapiro ** Can't simply find the first ':' is the address might be in the 185090795Sgshapiro ** form: "<@[IPv6:::1]:user@host>" and the first ':' in inside 185190795Sgshapiro ** the IPv6 address. 185290795Sgshapiro */ 185390795Sgshapiro 185490795Sgshapiro start = addr; 185590795Sgshapiro braclev = 0; 185690795Sgshapiro while (*start != '\0') 185790795Sgshapiro { 185890795Sgshapiro if (*start == ':' && braclev <= 0) 185990795Sgshapiro break; 186090795Sgshapiro else if (*start == '[') 186190795Sgshapiro braclev++; 186290795Sgshapiro else if (*start == ']' && braclev > 0) 186390795Sgshapiro braclev--; 186490795Sgshapiro start++; 186590795Sgshapiro } 186690795Sgshapiro if (braclev > 0 || *start != ':') 186790795Sgshapiro return false; 186890795Sgshapiro 186938032Speter at = strrchr(addr, '@'); 187090795Sgshapiro if (at == NULL || at < start) 187190795Sgshapiro return false; 187238032Speter 187338032Speter /* slice off the angle brackets */ 187438032Speter i = strlen(at + 1); 1875168520Sgshapiro if (i >= sizeof(hostbuf)) 187690795Sgshapiro return false; 1877168520Sgshapiro (void) sm_strlcpy(hostbuf, at + 1, sizeof(hostbuf)); 187838032Speter hostbuf[i - 1] = '\0'; 187938032Speter 188090795Sgshapiro while (start != NULL) 188138032Speter { 1882363466Sgshapiro if (getmxrr(hostbuf, mxhosts, NULL, TRYFALLBACK, &rcode, NULL, -1) 1883363466Sgshapiro > 0) 188438032Speter { 188590795Sgshapiro (void) sm_strlcpy(addr + 1, start + 1, 188690795Sgshapiro strlen(addr) - 1); 188790795Sgshapiro return true; 188838032Speter } 188938032Speter c = *start; 189038032Speter *start = '\0'; 189138032Speter comma = strrchr(addr, ','); 189238032Speter if (comma != NULL && comma[1] == '@' && 1893168520Sgshapiro strlen(comma + 2) < sizeof(hostbuf)) 1894168520Sgshapiro (void) sm_strlcpy(hostbuf, comma + 2, sizeof(hostbuf)); 189538032Speter else 189638032Speter comma = NULL; 189738032Speter *start = c; 189838032Speter start = comma; 189938032Speter } 190064565Sgshapiro#endif /* NAMED_BIND */ 190190795Sgshapiro return false; 190238032Speter} 1903