summaryrefslogtreecommitdiffstats
path: root/sendmail/src/savemail.c
diff options
context:
space:
mode:
Diffstat (limited to 'sendmail/src/savemail.c')
-rw-r--r--sendmail/src/savemail.c1761
1 files changed, 1761 insertions, 0 deletions
diff --git a/sendmail/src/savemail.c b/sendmail/src/savemail.c
new file mode 100644
index 0000000..cf72e8d
--- /dev/null
+++ b/sendmail/src/savemail.c
@@ -0,0 +1,1761 @@
+/*
+ * Copyright (c) 1998-2003, 2006 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sendmail.h>
+
+SM_RCSID("@(#)$Id: savemail.c,v 8.313 2006/11/29 00:20:41 ca Exp $")
+
+static bool errbody __P((MCI *, ENVELOPE *, char *));
+static bool pruneroute __P((char *));
+
+/*
+** SAVEMAIL -- Save mail on error
+**
+** If mailing back errors, mail it back to the originator
+** together with an error message; otherwise, just put it in
+** dead.letter in the user's home directory (if he exists on
+** this machine).
+**
+** Parameters:
+** e -- the envelope containing the message in error.
+** sendbody -- if true, also send back the body of the
+** message; otherwise just send the header.
+**
+** Returns:
+** true if savemail panic'ed, (i.e., the data file should
+** be preserved by dropenvelope())
+**
+** Side Effects:
+** Saves the letter, by writing or mailing it back to the
+** sender, or by putting it in dead.letter in her home
+** directory.
+*/
+
+/* defines for state machine */
+#define ESM_REPORT 0 /* report to sender's terminal */
+#define ESM_MAIL 1 /* mail back to sender */
+#define ESM_QUIET 2 /* mail has already been returned */
+#define ESM_DEADLETTER 3 /* save in ~/dead.letter */
+#define ESM_POSTMASTER 4 /* return to postmaster */
+#define ESM_DEADLETTERDROP 5 /* save in DeadLetterDrop */
+#define ESM_PANIC 6 /* call loseqfile() */
+#define ESM_DONE 7 /* message is successfully delivered */
+
+bool
+savemail(e, sendbody)
+ register ENVELOPE *e;
+ bool sendbody;
+{
+ register SM_FILE_T *fp;
+ bool panic = false;
+ int state;
+ auto ADDRESS *q = NULL;
+ register char *p;
+ MCI mcibuf;
+ int flags;
+ long sff;
+ char buf[MAXLINE + 1];
+ char dlbuf[MAXPATHLEN];
+ SM_MBDB_T user;
+
+
+ if (tTd(6, 1))
+ {
+ sm_dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=",
+ e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
+ ExitStat);
+ printaddr(sm_debug_file(), &e->e_from, false);
+ }
+
+ if (e->e_id == NULL)
+ {
+ /* can't return a message with no id */
+ return panic;
+ }
+
+ /*
+ ** In the unhappy event we don't know who to return the mail
+ ** to, make someone up.
+ */
+
+ if (e->e_from.q_paddr == NULL)
+ {
+ e->e_sender = "Postmaster";
+ if (parseaddr(e->e_sender, &e->e_from,
+ RF_COPYPARSE|RF_SENDERADDR,
+ '\0', NULL, e, false) == NULL)
+ {
+ syserr("553 5.3.5 Cannot parse Postmaster!");
+ finis(true, true, EX_SOFTWARE);
+ }
+ }
+ e->e_to = NULL;
+
+ /*
+ ** Basic state machine.
+ **
+ ** This machine runs through the following states:
+ **
+ ** ESM_QUIET Errors have already been printed iff the
+ ** sender is local.
+ ** ESM_REPORT Report directly to the sender's terminal.
+ ** ESM_MAIL Mail response to the sender.
+ ** ESM_DEADLETTER Save response in ~/dead.letter.
+ ** ESM_POSTMASTER Mail response to the postmaster.
+ ** ESM_DEADLETTERDROP
+ ** If DeadLetterDrop set, save it there.
+ ** ESM_PANIC Save response anywhere possible.
+ */
+
+ /* determine starting state */
+ switch (e->e_errormode)
+ {
+ case EM_WRITE:
+ state = ESM_REPORT;
+ break;
+
+ case EM_BERKNET:
+ case EM_MAIL:
+ state = ESM_MAIL;
+ break;
+
+ case EM_PRINT:
+ case '\0':
+ state = ESM_QUIET;
+ break;
+
+ case EM_QUIET:
+ /* no need to return anything at all */
+ return panic;
+
+ default:
+ syserr("554 5.3.0 savemail: bogus errormode x%x",
+ e->e_errormode);
+ state = ESM_MAIL;
+ break;
+ }
+
+ /* if this is already an error response, send to postmaster */
+ if (bitset(EF_RESPONSE, e->e_flags))
+ {
+ if (e->e_parent != NULL &&
+ bitset(EF_RESPONSE, e->e_parent->e_flags))
+ {
+ /* got an error sending a response -- can it */
+ return panic;
+ }
+ state = ESM_POSTMASTER;
+ }
+
+ while (state != ESM_DONE)
+ {
+ if (tTd(6, 5))
+ sm_dprintf(" state %d\n", state);
+
+ switch (state)
+ {
+ case ESM_QUIET:
+ if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
+ state = ESM_DEADLETTER;
+ else
+ state = ESM_MAIL;
+ break;
+
+ case ESM_REPORT:
+
+ /*
+ ** If the user is still logged in on the same terminal,
+ ** then write the error messages back to hir (sic).
+ */
+
+#if USE_TTYPATH
+ p = ttypath();
+#else /* USE_TTYPATH */
+ p = NULL;
+#endif /* USE_TTYPATH */
+
+ if (p == NULL || sm_io_reopen(SmFtStdio,
+ SM_TIME_DEFAULT,
+ p, SM_IO_WRONLY, NULL,
+ smioout) == NULL)
+ {
+ state = ESM_MAIL;
+ break;
+ }
+
+ expand("\201n", buf, sizeof(buf), e);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\r\nMessage from %s...\r\n", buf);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Errors occurred while sending mail.\r\n");
+ if (e->e_xfp != NULL)
+ {
+ (void) bfrewind(e->e_xfp);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Transcript follows:\r\n");
+ while (sm_io_fgets(e->e_xfp, SM_TIME_DEFAULT,
+ buf, sizeof(buf)) != NULL &&
+ !sm_io_error(smioout))
+ (void) sm_io_fputs(smioout,
+ SM_TIME_DEFAULT,
+ buf);
+ }
+ else
+ {
+ syserr("Cannot open %s",
+ queuename(e, XSCRPT_LETTER));
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Transcript of session is unavailable.\r\n");
+ }
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Original message will be saved in dead.letter.\r\n");
+ state = ESM_DEADLETTER;
+ break;
+
+ case ESM_MAIL:
+ /*
+ ** If mailing back, do it.
+ ** Throw away all further output. Don't alias,
+ ** since this could cause loops, e.g., if joe
+ ** mails to joe@x, and for some reason the network
+ ** for @x is down, then the response gets sent to
+ ** joe@x, which gives a response, etc. Also force
+ ** the mail to be delivered even if a version of
+ ** it has already been sent to the sender.
+ **
+ ** If this is a configuration or local software
+ ** error, send to the local postmaster as well,
+ ** since the originator can't do anything
+ ** about it anyway. Note that this is a full
+ ** copy of the message (intentionally) so that
+ ** the Postmaster can forward things along.
+ */
+
+ if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
+ {
+ (void) sendtolist("postmaster", NULLADDR,
+ &e->e_errorqueue, 0, e);
+ }
+ if (!emptyaddr(&e->e_from))
+ {
+ char from[TOBUFSIZE];
+
+ if (sm_strlcpy(from, e->e_from.q_paddr,
+ sizeof(from)) >= sizeof(from))
+ {
+ state = ESM_POSTMASTER;
+ break;
+ }
+
+ if (!DontPruneRoutes)
+ (void) pruneroute(from);
+
+ (void) sendtolist(from, NULLADDR,
+ &e->e_errorqueue, 0, e);
+ }
+
+ /*
+ ** Deliver a non-delivery report to the
+ ** Postmaster-designate (not necessarily
+ ** Postmaster). This does not include the
+ ** body of the message, for privacy reasons.
+ ** You really shouldn't need this.
+ */
+
+ e->e_flags |= EF_PM_NOTIFY;
+
+ /* check to see if there are any good addresses */
+ for (q = e->e_errorqueue; q != NULL; q = q->q_next)
+ {
+ if (QS_IS_SENDABLE(q->q_state))
+ break;
+ }
+ if (q == NULL)
+ {
+ /* this is an error-error */
+ state = ESM_POSTMASTER;
+ break;
+ }
+ if (returntosender(e->e_message, e->e_errorqueue,
+ sendbody ? RTSF_SEND_BODY
+ : RTSF_NO_BODY,
+ e) == 0)
+ {
+ state = ESM_DONE;
+ break;
+ }
+
+ /* didn't work -- return to postmaster */
+ state = ESM_POSTMASTER;
+ break;
+
+ case ESM_POSTMASTER:
+ /*
+ ** Similar to previous case, but to system postmaster.
+ */
+
+ q = NULL;
+ expand(DoubleBounceAddr, buf, sizeof(buf), e);
+
+ /*
+ ** Just drop it on the floor if DoubleBounceAddr
+ ** expands to an empty string.
+ */
+
+ if (*buf == '\0')
+ {
+ state = ESM_DONE;
+ break;
+ }
+ if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0)
+ {
+ syserr("553 5.3.0 cannot parse %s!", buf);
+ ExitStat = EX_SOFTWARE;
+ state = ESM_DEADLETTERDROP;
+ break;
+ }
+ flags = RTSF_PM_BOUNCE;
+ if (sendbody)
+ flags |= RTSF_SEND_BODY;
+ if (returntosender(e->e_message, q, flags, e) == 0)
+ {
+ state = ESM_DONE;
+ break;
+ }
+
+ /* didn't work -- last resort */
+ state = ESM_DEADLETTERDROP;
+ break;
+
+ case ESM_DEADLETTER:
+ /*
+ ** Save the message in dead.letter.
+ ** If we weren't mailing back, and the user is
+ ** local, we should save the message in
+ ** ~/dead.letter so that the poor person doesn't
+ ** have to type it over again -- and we all know
+ ** what poor typists UNIX users are.
+ */
+
+ p = NULL;
+ if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
+ {
+ if (e->e_from.q_home != NULL)
+ p = e->e_from.q_home;
+ else if (sm_mbdb_lookup(e->e_from.q_user, &user)
+ == EX_OK &&
+ *user.mbdb_homedir != '\0')
+ p = user.mbdb_homedir;
+ }
+ if (p == NULL || e->e_dfp == NULL)
+ {
+ /* no local directory or no data file */
+ state = ESM_MAIL;
+ break;
+ }
+
+ /* we have a home directory; write dead.letter */
+ macdefine(&e->e_macro, A_TEMP, 'z', p);
+
+ /* get the sender for the UnixFromLine */
+ p = macvalue('g', e);
+ macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
+
+ expand("\201z/dead.letter", dlbuf, sizeof(dlbuf), e);
+ sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
+ if (RealUid == 0)
+ sff |= SFF_ROOTOK;
+ e->e_to = dlbuf;
+ if (writable(dlbuf, NULL, sff) &&
+ mailfile(dlbuf, FileMailer, NULL, sff, e) == EX_OK)
+ {
+ int oldverb = Verbose;
+
+ if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
+ Verbose = 1;
+ if (Verbose > 0)
+ message("Saved message in %s", dlbuf);
+ Verbose = oldverb;
+ macdefine(&e->e_macro, A_PERM, 'g', p);
+ state = ESM_DONE;
+ break;
+ }
+ macdefine(&e->e_macro, A_PERM, 'g', p);
+ state = ESM_MAIL;
+ break;
+
+ case ESM_DEADLETTERDROP:
+ /*
+ ** Log the mail in DeadLetterDrop file.
+ */
+
+ if (e->e_class < 0)
+ {
+ state = ESM_DONE;
+ break;
+ }
+
+ if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') ||
+ DeadLetterDrop == NULL ||
+ DeadLetterDrop[0] == '\0')
+ {
+ state = ESM_PANIC;
+ break;
+ }
+
+ sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN;
+ if (!writable(DeadLetterDrop, NULL, sff) ||
+ (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND,
+ FileMode, sff)) == NULL)
+ {
+ state = ESM_PANIC;
+ break;
+ }
+
+ memset(&mcibuf, '\0', sizeof(mcibuf));
+ mcibuf.mci_out = fp;
+ mcibuf.mci_mailer = FileMailer;
+ if (bitnset(M_7BITS, FileMailer->m_flags))
+ mcibuf.mci_flags |= MCIF_7BIT;
+
+ /* get the sender for the UnixFromLine */
+ p = macvalue('g', e);
+ macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
+
+ if (!putfromline(&mcibuf, e) ||
+ !(*e->e_puthdr)(&mcibuf, e->e_header, e,
+ M87F_OUTER) ||
+ !(*e->e_putbody)(&mcibuf, e, NULL) ||
+ !putline("\n", &mcibuf) ||
+ sm_io_flush(fp, SM_TIME_DEFAULT) == SM_IO_EOF ||
+ sm_io_error(fp) ||
+ sm_io_close(fp, SM_TIME_DEFAULT) < 0)
+ state = ESM_PANIC;
+ else
+ {
+ int oldverb = Verbose;
+
+ if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
+ Verbose = 1;
+ if (Verbose > 0)
+ message("Saved message in %s",
+ DeadLetterDrop);
+ Verbose = oldverb;
+ if (LogLevel > 3)
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "Saved message in %s",
+ DeadLetterDrop);
+ state = ESM_DONE;
+ }
+ macdefine(&e->e_macro, A_PERM, 'g', p);
+ break;
+
+ default:
+ syserr("554 5.3.5 savemail: unknown state %d", state);
+ /* FALLTHROUGH */
+
+ case ESM_PANIC:
+ /* leave the locked queue & transcript files around */
+ loseqfile(e, "savemail panic");
+ panic = true;
+ errno = 0;
+ syserr("554 savemail: cannot save rejected email anywhere");
+ state = ESM_DONE;
+ break;
+ }
+ }
+ return panic;
+}
+/*
+** RETURNTOSENDER -- return a message to the sender with an error.
+**
+** Parameters:
+** msg -- the explanatory message.
+** returnq -- the queue of people to send the message to.
+** flags -- flags tweaking the operation:
+** RTSF_SENDBODY -- include body of message (otherwise
+** just send the header).
+** RTSF_PMBOUNCE -- this is a postmaster bounce.
+** e -- the current envelope.
+**
+** Returns:
+** zero -- if everything went ok.
+** else -- some error.
+**
+** Side Effects:
+** Returns the current message to the sender via mail.
+*/
+
+#define MAXRETURNS 6 /* max depth of returning messages */
+#define ERRORFUDGE 1024 /* nominal size of error message text */
+
+int
+returntosender(msg, returnq, flags, e)
+ char *msg;
+ ADDRESS *returnq;
+ int flags;
+ register ENVELOPE *e;
+{
+ register ENVELOPE *ee;
+ ENVELOPE *oldcur = CurEnv;
+ ENVELOPE errenvelope;
+ static int returndepth = 0;
+ register ADDRESS *q;
+ char *p;
+ char buf[MAXNAME + 1];
+
+ if (returnq == NULL)
+ return -1;
+
+ if (msg == NULL)
+ msg = "Unable to deliver mail";
+
+ if (tTd(6, 1))
+ {
+ sm_dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%p, returnq=",
+ msg, returndepth, e);
+ printaddr(sm_debug_file(), returnq, true);
+ if (tTd(6, 20))
+ {
+ sm_dprintf("Sendq=");
+ printaddr(sm_debug_file(), e->e_sendqueue, true);
+ }
+ }
+
+ if (++returndepth >= MAXRETURNS)
+ {
+ if (returndepth != MAXRETURNS)
+ syserr("554 5.3.0 returntosender: infinite recursion on %s",
+ returnq->q_paddr);
+ /* don't "unrecurse" and fake a clean exit */
+ /* returndepth--; */
+ return 0;
+ }
+
+ macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
+ macdefine(&e->e_macro, A_PERM, 'u', NULL);
+
+ /* initialize error envelope */
+ ee = newenvelope(&errenvelope, e, sm_rpool_new_x(NULL));
+ macdefine(&ee->e_macro, A_PERM, 'a', "\201b");
+ macdefine(&ee->e_macro, A_PERM, 'r', "");
+ macdefine(&ee->e_macro, A_PERM, 's', "localhost");
+ macdefine(&ee->e_macro, A_PERM, '_', "localhost");
+ clrsessenvelope(ee);
+
+ ee->e_puthdr = putheader;
+ ee->e_putbody = errbody;
+ ee->e_flags |= EF_RESPONSE|EF_METOO;
+ if (!bitset(EF_OLDSTYLE, e->e_flags))
+ ee->e_flags &= ~EF_OLDSTYLE;
+ if (bitset(EF_DONT_MIME, e->e_flags))
+ {
+ ee->e_flags |= EF_DONT_MIME;
+
+ /*
+ ** If we can't convert to MIME and we don't pass
+ ** 8-bit, we can't send the body.
+ */
+
+ if (bitset(EF_HAS8BIT, e->e_flags) &&
+ !bitset(MM_PASS8BIT, MimeMode))
+ flags &= ~RTSF_SEND_BODY;
+ }
+
+ ee->e_sendqueue = returnq;
+ ee->e_msgsize = 0;
+ if (bitset(RTSF_SEND_BODY, flags) &&
+ !bitset(PRIV_NOBODYRETN, PrivacyFlags))
+ ee->e_msgsize = ERRORFUDGE + e->e_msgsize;
+ else
+ ee->e_flags |= EF_NO_BODY_RETN;
+
+ if (!setnewqueue(ee))
+ {
+ syserr("554 5.3.0 returntosender: cannot select queue for %s",
+ returnq->q_paddr);
+ ExitStat = EX_UNAVAILABLE;
+ returndepth--;
+ return -1;
+ }
+ initsys(ee);
+
+#if NAMED_BIND
+ _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
+ _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
+#endif /* NAMED_BIND */
+ for (q = returnq; q != NULL; q = q->q_next)
+ {
+ if (QS_IS_BADADDR(q->q_state))
+ continue;
+
+ q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
+ q->q_flags |= QPINGONFAILURE;
+
+ if (!QS_IS_DEAD(q->q_state))
+ ee->e_nrcpts++;
+
+ if (q->q_alias == NULL)
+ addheader("To", q->q_paddr, 0, ee, true);
+ }
+
+ if (LogLevel > 5)
+ {
+ if (bitset(EF_RESPONSE, e->e_flags))
+ p = "return to sender";
+ else if (bitset(EF_WARNING, e->e_flags))
+ p = "sender notify";
+ else if (bitset(RTSF_PM_BOUNCE, flags))
+ p = "postmaster notify";
+ else
+ p = "DSN";
+ sm_syslog(LOG_INFO, e->e_id, "%s: %s: %s",
+ ee->e_id, p, shortenstring(msg, MAXSHORTSTR));
+ }
+
+ if (SendMIMEErrors)
+ {
+ addheader("MIME-Version", "1.0", 0, ee, true);
+ (void) sm_snprintf(buf, sizeof(buf), "%s.%ld/%.100s",
+ ee->e_id, (long)curtime(), MyHostName);
+ ee->e_msgboundary = sm_rpool_strdup_x(ee->e_rpool, buf);
+ (void) sm_snprintf(buf, sizeof(buf),
+#if DSN
+ "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"",
+#else /* DSN */
+ "multipart/mixed; boundary=\"%s\"",
+#endif /* DSN */
+ ee->e_msgboundary);
+ addheader("Content-Type", buf, 0, ee, true);
+
+ p = hvalue("Content-Transfer-Encoding", e->e_header);
+ if (p != NULL && sm_strcasecmp(p, "binary") != 0)
+ p = NULL;
+ if (p == NULL && bitset(EF_HAS8BIT, e->e_flags))
+ p = "8bit";
+ if (p != NULL)
+ addheader("Content-Transfer-Encoding", p, 0, ee, true);
+ }
+ if (strncmp(msg, "Warning:", 8) == 0)
+ {
+ addheader("Subject", msg, 0, ee, true);
+ p = "warning-timeout";
+ }
+ else if (strncmp(msg, "Postmaster warning:", 19) == 0)
+ {
+ addheader("Subject", msg, 0, ee, true);
+ p = "postmaster-warning";
+ }
+ else if (strcmp(msg, "Return receipt") == 0)
+ {
+ addheader("Subject", msg, 0, ee, true);
+ p = "return-receipt";
+ }
+ else if (bitset(RTSF_PM_BOUNCE, flags))
+ {
+ (void) sm_snprintf(buf, sizeof(buf),
+ "Postmaster notify: see transcript for details");
+ addheader("Subject", buf, 0, ee, true);
+ p = "postmaster-notification";
+ }
+ else
+ {
+ (void) sm_snprintf(buf, sizeof(buf),
+ "Returned mail: see transcript for details");
+ addheader("Subject", buf, 0, ee, true);
+ p = "failure";
+ }
+ (void) sm_snprintf(buf, sizeof(buf), "auto-generated (%s)", p);
+ addheader("Auto-Submitted", buf, 0, ee, true);
+
+ /* fake up an address header for the from person */
+ expand("\201n", buf, sizeof(buf), e);
+ if (parseaddr(buf, &ee->e_from,
+ RF_COPYALL|RF_SENDERADDR, '\0', NULL, e, false) == NULL)
+ {
+ syserr("553 5.3.5 Can't parse myself!");
+ ExitStat = EX_SOFTWARE;
+ returndepth--;
+ return -1;
+ }
+ ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
+ ee->e_from.q_flags |= QPINGONFAILURE;
+ ee->e_sender = ee->e_from.q_paddr;
+
+ /* push state into submessage */
+ CurEnv = ee;
+ macdefine(&ee->e_macro, A_PERM, 'f', "\201n");
+ macdefine(&ee->e_macro, A_PERM, 'x', "Mail Delivery Subsystem");
+ eatheader(ee, true, true);
+
+ /* mark statistics */
+ markstats(ee, NULLADDR, STATS_NORMAL);
+
+ /* actually deliver the error message */
+ sendall(ee, SM_DELIVER);
+
+ /* restore state */
+ dropenvelope(ee, true, false);
+ sm_rpool_free(ee->e_rpool);
+ CurEnv = oldcur;
+ returndepth--;
+
+ /* check for delivery errors */
+ if (ee->e_parent == NULL ||
+ !bitset(EF_RESPONSE, ee->e_parent->e_flags))
+ return 0;
+ for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (QS_IS_ATTEMPTED(q->q_state))
+ return 0;
+ }
+ return -1;
+}
+/*
+** ERRBODY -- output the body of an error message.
+**
+** Typically this is a copy of the transcript plus a copy of the
+** original offending message.
+**
+** Parameters:
+** mci -- the mailer connection information.
+** e -- the envelope we are working in.
+** separator -- any possible MIME separator (unused).
+**
+** Returns:
+** true iff body was written successfully
+**
+** Side Effects:
+** Outputs the body of an error message.
+*/
+
+/* ARGSUSED2 */
+static bool
+errbody(mci, e, separator)
+ register MCI *mci;
+ register ENVELOPE *e;
+ char *separator;
+{
+ bool printheader;
+ bool sendbody;
+ bool pm_notify;
+ int save_errno;
+ register SM_FILE_T *xfile;
+ char *p;
+ register ADDRESS *q = NULL;
+ char actual[MAXLINE];
+ char buf[MAXLINE];
+
+ if (bitset(MCIF_INHEADER, mci->mci_flags))
+ {
+ if (!putline("", mci))
+ goto writeerr;
+ mci->mci_flags &= ~MCIF_INHEADER;
+ }
+ if (e->e_parent == NULL)
+ {
+ syserr("errbody: null parent");
+ if (!putline(" ----- Original message lost -----\n", mci))
+ goto writeerr;
+ return true;
+ }
+
+ /*
+ ** Output MIME header.
+ */
+
+ if (e->e_msgboundary != NULL)
+ {
+ (void) sm_strlcpyn(buf, sizeof(buf), 2, "--", e->e_msgboundary);
+ if (!putline("This is a MIME-encapsulated message", mci) ||
+ !putline("", mci) ||
+ !putline(buf, mci) ||
+ !putline("", mci))
+ goto writeerr;
+ }
+
+ /*
+ ** Output introductory information.
+ */
+
+ pm_notify = false;
+ p = hvalue("subject", e->e_header);
+ if (p != NULL && strncmp(p, "Postmaster ", 11) == 0)
+ pm_notify = true;
+ else
+ {
+ for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (QS_IS_BADADDR(q->q_state))
+ break;
+ }
+ }
+ if (!pm_notify && q == NULL &&
+ !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
+ {
+ if (!putline(" **********************************************",
+ mci) ||
+ !putline(" ** THIS IS A WARNING MESSAGE ONLY **",
+ mci) ||
+ !putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **",
+ mci) ||
+ !putline(" **********************************************",
+ mci) ||
+ !putline("", mci))
+ goto writeerr;
+ }
+ (void) sm_snprintf(buf, sizeof(buf),
+ "The original message was received at %s",
+ arpadate(ctime(&e->e_parent->e_ctime)));
+ if (!putline(buf, mci))
+ goto writeerr;
+ expand("from \201_", buf, sizeof(buf), e->e_parent);
+ if (!putline(buf, mci))
+ goto writeerr;
+
+ /* include id in postmaster copies */
+ if (pm_notify && e->e_parent->e_id != NULL)
+ {
+ (void) sm_strlcpyn(buf, sizeof(buf), 2, "with id ",
+ e->e_parent->e_id);
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+ if (!putline("", mci))
+ goto writeerr;
+
+ /*
+ ** Output error message header (if specified and available).
+ */
+
+ if (ErrMsgFile != NULL &&
+ !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
+ {
+ if (*ErrMsgFile == '/')
+ {
+ long sff = SFF_ROOTOK|SFF_REGONLY;
+
+ if (DontLockReadFiles)
+ sff |= SFF_NOLOCK;
+ if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH,
+ DontBlameSendmail))
+ sff |= SFF_SAFEDIRPATH;
+ xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff);
+ if (xfile != NULL)
+ {
+ while (sm_io_fgets(xfile, SM_TIME_DEFAULT, buf,
+ sizeof(buf)) != NULL)
+ {
+ int lbs;
+ bool putok;
+ char *lbp;
+
+ lbs = sizeof(buf);
+ lbp = translate_dollars(buf, buf, &lbs);
+ expand(lbp, lbp, lbs, e);
+ putok = putline(lbp, mci);
+ if (lbp != buf)
+ sm_free(lbp);
+ if (!putok)
+ goto writeerr;
+ }
+ (void) sm_io_close(xfile, SM_TIME_DEFAULT);
+ if (!putline("\n", mci))
+ goto writeerr;
+ }
+ }
+ else
+ {
+ expand(ErrMsgFile, buf, sizeof(buf), e);
+ if (!putline(buf, mci) || !putline("", mci))
+ goto writeerr;
+ }
+ }
+
+ /*
+ ** Output message introduction
+ */
+
+ /* permanent fatal errors */
+ printheader = true;
+ for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (!QS_IS_BADADDR(q->q_state) ||
+ !bitset(QPINGONFAILURE, q->q_flags))
+ continue;
+
+ if (printheader)
+ {
+ if (!putline(" ----- The following addresses had permanent fatal errors -----",
+ mci))
+ goto writeerr;
+ printheader = false;
+ }
+
+ (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
+ sizeof(buf));
+ if (!putline(buf, mci))
+ goto writeerr;
+ if (q->q_rstatus != NULL)
+ {
+ (void) sm_snprintf(buf, sizeof(buf),
+ " (reason: %s)",
+ shortenstring(exitstat(q->q_rstatus),
+ MAXSHORTSTR));
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+ if (q->q_alias != NULL)
+ {
+ (void) sm_snprintf(buf, sizeof(buf),
+ " (expanded from: %s)",
+ shortenstring(q->q_alias->q_paddr,
+ MAXSHORTSTR));
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+ }
+ if (!printheader && !putline("", mci))
+ goto writeerr;
+
+ /* transient non-fatal errors */
+ printheader = true;
+ for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (QS_IS_BADADDR(q->q_state) ||
+ !bitset(QPRIMARY, q->q_flags) ||
+ !bitset(QBYNDELAY, q->q_flags) ||
+ !bitset(QDELAYED, q->q_flags))
+ continue;
+
+ if (printheader)
+ {
+ if (!putline(" ----- The following addresses had transient non-fatal errors -----",
+ mci))
+ goto writeerr;
+ printheader = false;
+ }
+
+ (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
+ sizeof(buf));
+ if (!putline(buf, mci))
+ goto writeerr;
+ if (q->q_alias != NULL)
+ {
+ (void) sm_snprintf(buf, sizeof(buf),
+ " (expanded from: %s)",
+ shortenstring(q->q_alias->q_paddr,
+ MAXSHORTSTR));
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+ }
+ if (!printheader && !putline("", mci))
+ goto writeerr;
+
+ /* successful delivery notifications */
+ printheader = true;
+ for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (QS_IS_BADADDR(q->q_state) ||
+ !bitset(QPRIMARY, q->q_flags) ||
+ bitset(QBYNDELAY, q->q_flags) ||
+ bitset(QDELAYED, q->q_flags))
+ continue;
+ else if (bitset(QBYNRELAY, q->q_flags))
+ p = "Deliver-By notify: relayed";
+ else if (bitset(QBYTRACE, q->q_flags))
+ p = "Deliver-By trace: relayed";
+ else if (!bitset(QPINGONSUCCESS, q->q_flags))
+ continue;
+ else if (bitset(QRELAYED, q->q_flags))
+ p = "relayed to non-DSN-aware mailer";
+ else if (bitset(QDELIVERED, q->q_flags))
+ {
+ if (bitset(QEXPANDED, q->q_flags))
+ p = "successfully delivered to mailing list";
+ else
+ p = "successfully delivered to mailbox";
+ }
+ else if (bitset(QEXPANDED, q->q_flags))
+ p = "expanded by alias";
+ else
+ continue;
+
+ if (printheader)
+ {
+ if (!putline(" ----- The following addresses had successful delivery notifications -----",
+ mci))
+ goto writeerr;
+ printheader = false;
+ }
+
+ (void) sm_snprintf(buf, sizeof(buf), "%s (%s)",
+ shortenstring(q->q_paddr, MAXSHORTSTR), p);
+ if (!putline(buf, mci))
+ goto writeerr;
+ if (q->q_alias != NULL)
+ {
+ (void) sm_snprintf(buf, sizeof(buf),
+ " (expanded from: %s)",
+ shortenstring(q->q_alias->q_paddr,
+ MAXSHORTSTR));
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+ }
+ if (!printheader && !putline("", mci))
+ goto writeerr;
+
+ /*
+ ** Output transcript of errors
+ */
+
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
+ if (e->e_parent->e_xfp == NULL)
+ {
+ if (!putline(" ----- Transcript of session is unavailable -----\n",
+ mci))
+ goto writeerr;
+ }
+ else
+ {
+ printheader = true;
+ (void) bfrewind(e->e_parent->e_xfp);
+ if (e->e_xfp != NULL)
+ (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
+ while (sm_io_fgets(e->e_parent->e_xfp, SM_TIME_DEFAULT, buf,
+ sizeof(buf)) != NULL)
+ {
+ if (printheader && !putline(" ----- Transcript of session follows -----\n",
+ mci))
+ goto writeerr;
+ printheader = false;
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+ }
+ errno = 0;
+
+#if DSN
+ /*
+ ** Output machine-readable version.
+ */
+
+ if (e->e_msgboundary != NULL)
+ {
+ (void) sm_strlcpyn(buf, sizeof(buf), 2, "--", e->e_msgboundary);
+ if (!putline("", mci) ||
+ !putline(buf, mci) ||
+ !putline("Content-Type: message/delivery-status", mci) ||
+ !putline("", mci))
+ goto writeerr;
+
+ /*
+ ** Output per-message information.
+ */
+
+ /* original envelope id from MAIL FROM: line */
+ if (e->e_parent->e_envid != NULL)
+ {
+ (void) sm_snprintf(buf, sizeof(buf),
+ "Original-Envelope-Id: %.800s",
+ xuntextify(e->e_parent->e_envid));
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+
+ /* Reporting-MTA: is us (required) */
+ (void) sm_snprintf(buf, sizeof(buf),
+ "Reporting-MTA: dns; %.800s", MyHostName);
+ if (!putline(buf, mci))
+ goto writeerr;
+
+ /* DSN-Gateway: not relevant since we are not translating */
+
+ /* Received-From-MTA: shows where we got this message from */
+ if (RealHostName != NULL)
+ {
+ /* XXX use $s for type? */
+ if (e->e_parent->e_from.q_mailer == NULL ||
+ (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL)
+ p = "dns";
+ (void) sm_snprintf(buf, sizeof(buf),
+ "Received-From-MTA: %s; %.800s",
+ p, RealHostName);
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+
+ /* Arrival-Date: -- when it arrived here */
+ (void) sm_strlcpyn(buf, sizeof(buf), 2, "Arrival-Date: ",
+ arpadate(ctime(&e->e_parent->e_ctime)));
+ if (!putline(buf, mci))
+ goto writeerr;
+
+ /* Deliver-By-Date: -- when it should have been delivered */
+ if (IS_DLVR_BY(e->e_parent))
+ {
+ time_t dbyd;
+
+ dbyd = e->e_parent->e_ctime + e->e_parent->e_deliver_by;
+ (void) sm_strlcpyn(buf, sizeof(buf), 2,
+ "Deliver-By-Date: ",
+ arpadate(ctime(&dbyd)));
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+
+ /*
+ ** Output per-address information.
+ */
+
+ for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ char *action;
+
+ if (QS_IS_BADADDR(q->q_state))
+ {
+ /* RFC 1891, 6.2.6 (b) */
+ if (bitset(QHASNOTIFY, q->q_flags) &&
+ !bitset(QPINGONFAILURE, q->q_flags))
+ continue;
+ action = "failed";
+ }
+ else if (!bitset(QPRIMARY, q->q_flags))
+ continue;
+ else if (bitset(QDELIVERED, q->q_flags))
+ {
+ if (bitset(QEXPANDED, q->q_flags))
+ action = "delivered (to mailing list)";
+ else
+ action = "delivered (to mailbox)";
+ }
+ else if (bitset(QRELAYED, q->q_flags))
+ action = "relayed (to non-DSN-aware mailer)";
+ else if (bitset(QEXPANDED, q->q_flags))
+ action = "expanded (to multi-recipient alias)";
+ else if (bitset(QDELAYED, q->q_flags))
+ action = "delayed";
+ else if (bitset(QBYTRACE, q->q_flags))
+ action = "relayed (Deliver-By trace mode)";
+ else if (bitset(QBYNDELAY, q->q_flags))
+ action = "delayed (Deliver-By notify mode)";
+ else if (bitset(QBYNRELAY, q->q_flags))
+ action = "relayed (Deliver-By notify mode)";
+ else
+ continue;
+
+ if (!putline("", mci))
+ goto writeerr;
+
+ /* Original-Recipient: -- passed from on high */
+ if (q->q_orcpt != NULL)
+ {
+ (void) sm_snprintf(buf, sizeof(buf),
+ "Original-Recipient: %.800s",
+ q->q_orcpt);
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+
+ /* Figure out actual recipient */
+ actual[0] = '\0';
+ if (q->q_user[0] != '\0')
+ {
+ if (q->q_mailer != NULL &&
+ q->q_mailer->m_addrtype != NULL)
+ p = q->q_mailer->m_addrtype;
+ else
+ p = "rfc822";
+
+ if (sm_strcasecmp(p, "rfc822") == 0 &&
+ strchr(q->q_user, '@') == NULL)
+ {
+ (void) sm_snprintf(actual,
+ sizeof(actual),
+ "%s; %.700s@%.100s",
+ p, q->q_user,
+ MyHostName);
+ }
+ else
+ {
+ (void) sm_snprintf(actual,
+ sizeof(actual),
+ "%s; %.800s",
+ p, q->q_user);
+ }
+ }
+
+ /* Final-Recipient: -- the name from the RCPT command */
+ if (q->q_finalrcpt == NULL)
+ {
+ /* should never happen */
+ sm_syslog(LOG_ERR, e->e_id,
+ "returntosender: q_finalrcpt is NULL");
+
+ /* try to fall back to the actual recipient */
+ if (actual[0] != '\0')
+ q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool,
+ actual);
+ }
+
+ if (q->q_finalrcpt != NULL)
+ {
+ (void) sm_snprintf(buf, sizeof(buf),
+ "Final-Recipient: %s",
+ q->q_finalrcpt);
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+
+ /* X-Actual-Recipient: -- the real problem address */
+ if (actual[0] != '\0' &&
+ q->q_finalrcpt != NULL &&
+ !bitset(PRIV_NOACTUALRECIPIENT, PrivacyFlags) &&
+ strcmp(actual, q->q_finalrcpt) != 0)
+ {
+ (void) sm_snprintf(buf, sizeof(buf),
+ "X-Actual-Recipient: %s",
+ actual);
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+
+ /* Action: -- what happened? */
+ (void) sm_strlcpyn(buf, sizeof(buf), 2, "Action: ",
+ action);
+ if (!putline(buf, mci))
+ goto writeerr;
+
+ /* Status: -- what _really_ happened? */
+ if (q->q_status != NULL)
+ p = q->q_status;
+ else if (QS_IS_BADADDR(q->q_state))
+ p = "5.0.0";
+ else if (QS_IS_QUEUEUP(q->q_state))
+ p = "4.0.0";
+ else
+ p = "2.0.0";
+ (void) sm_strlcpyn(buf, sizeof(buf), 2, "Status: ", p);
+ if (!putline(buf, mci))
+ goto writeerr;
+
+ /* Remote-MTA: -- who was I talking to? */
+ if (q->q_statmta != NULL)
+ {
+ if (q->q_mailer == NULL ||
+ (p = q->q_mailer->m_mtatype) == NULL)
+ p = "dns";
+ (void) sm_snprintf(buf, sizeof(buf),
+ "Remote-MTA: %s; %.800s",
+ p, q->q_statmta);
+ p = &buf[strlen(buf) - 1];
+ if (*p == '.')
+ *p = '\0';
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+
+ /* Diagnostic-Code: -- actual result from other end */
+ if (q->q_rstatus != NULL)
+ {
+ if (q->q_mailer == NULL ||
+ (p = q->q_mailer->m_diagtype) == NULL)
+ p = "smtp";
+ (void) sm_snprintf(buf, sizeof(buf),
+ "Diagnostic-Code: %s; %.800s",
+ p, q->q_rstatus);
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+
+ /* Last-Attempt-Date: -- fine granularity */
+ if (q->q_statdate == (time_t) 0L)
+ q->q_statdate = curtime();
+ (void) sm_strlcpyn(buf, sizeof(buf), 2,
+ "Last-Attempt-Date: ",
+ arpadate(ctime(&q->q_statdate)));
+ if (!putline(buf, mci))
+ goto writeerr;
+
+ /* Will-Retry-Until: -- for delayed messages only */
+ if (QS_IS_QUEUEUP(q->q_state))
+ {
+ time_t xdate;
+
+ xdate = e->e_parent->e_ctime +
+ TimeOuts.to_q_return[e->e_parent->e_timeoutclass];
+ (void) sm_strlcpyn(buf, sizeof(buf), 2,
+ "Will-Retry-Until: ",
+ arpadate(ctime(&xdate)));
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+ }
+ }
+#endif /* DSN */
+
+ /*
+ ** Output text of original message
+ */
+
+ if (!putline("", mci))
+ goto writeerr;
+ if (bitset(EF_HAS_DF, e->e_parent->e_flags))
+ {
+ sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) &&
+ !bitset(EF_NO_BODY_RETN, e->e_flags);
+
+ if (e->e_msgboundary == NULL)
+ {
+ if (!putline(
+ sendbody
+ ? " ----- Original message follows -----\n"
+ : " ----- Message header follows -----\n",
+ mci))
+ {
+ goto writeerr;
+ }
+ }
+ else
+ {
+ (void) sm_strlcpyn(buf, sizeof(buf), 2, "--",
+ e->e_msgboundary);
+
+ if (!putline(buf, mci))
+ goto writeerr;
+ (void) sm_strlcpyn(buf, sizeof(buf), 2, "Content-Type: ",
+ sendbody ? "message/rfc822"
+ : "text/rfc822-headers");
+ if (!putline(buf, mci))
+ goto writeerr;
+
+ p = hvalue("Content-Transfer-Encoding",
+ e->e_parent->e_header);
+ if (p != NULL && sm_strcasecmp(p, "binary") != 0)
+ p = NULL;
+ if (p == NULL &&
+ bitset(EF_HAS8BIT, e->e_parent->e_flags))
+ p = "8bit";
+ if (p != NULL)
+ {
+ (void) sm_snprintf(buf, sizeof(buf),
+ "Content-Transfer-Encoding: %s",
+ p);
+ if (!putline(buf, mci))
+ goto writeerr;
+ }
+ }
+ if (!putline("", mci))
+ goto writeerr;
+ save_errno = errno;
+ if (!putheader(mci, e->e_parent->e_header, e->e_parent,
+ M87F_OUTER))
+ goto writeerr;
+ errno = save_errno;
+ if (sendbody)
+ {
+ if (!putbody(mci, e->e_parent, e->e_msgboundary))
+ goto writeerr;
+ }
+ else if (e->e_msgboundary == NULL)
+ {
+ if (!putline("", mci) ||
+ !putline(" ----- Message body suppressed -----",
+ mci))
+ {
+ goto writeerr;
+ }
+ }
+ }
+ else if (e->e_msgboundary == NULL)
+ {
+ if (!putline(" ----- No message was collected -----\n", mci))
+ goto writeerr;
+ }
+
+ if (e->e_msgboundary != NULL)
+ {
+ (void) sm_strlcpyn(buf, sizeof(buf), 3, "--", e->e_msgboundary,
+ "--");
+ if (!putline("", mci) || !putline(buf, mci))
+ goto writeerr;
+ }
+ if (!putline("", mci) ||
+ sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF)
+ goto writeerr;
+
+ /*
+ ** Cleanup and exit
+ */
+
+ if (errno != 0)
+ {
+ writeerr:
+ syserr("errbody: I/O error");
+ return false;
+ }
+ return true;
+}
+
+/*
+** SMTPTODSN -- convert SMTP to DSN status code
+**
+** Parameters:
+** smtpstat -- the smtp status code (e.g., 550).
+**
+** Returns:
+** The DSN version of the status code.
+**
+** Storage Management:
+** smtptodsn() returns a pointer to a character string literal,
+** which will remain valid forever, and thus does not need to
+** be copied. Current code relies on this property.
+*/
+
+char *
+smtptodsn(smtpstat)
+ int smtpstat;
+{
+ if (smtpstat < 0)
+ return "4.4.2";
+
+ switch (smtpstat)
+ {
+ case 450: /* Req mail action not taken: mailbox unavailable */
+ return "4.2.0";
+
+ case 451: /* Req action aborted: local error in processing */
+ return "4.3.0";
+
+ case 452: /* Req action not taken: insufficient sys storage */
+ return "4.3.1";
+
+ case 500: /* Syntax error, command unrecognized */
+ return "5.5.2";
+
+ case 501: /* Syntax error in parameters or arguments */
+ return "5.5.4";
+
+ case 502: /* Command not implemented */
+ return "5.5.1";
+
+ case 503: /* Bad sequence of commands */
+ return "5.5.1";
+
+ case 504: /* Command parameter not implemented */
+ return "5.5.4";
+
+ case 550: /* Req mail action not taken: mailbox unavailable */
+ return "5.2.0";
+
+ case 551: /* User not local; please try <...> */
+ return "5.1.6";
+
+ case 552: /* Req mail action aborted: exceeded storage alloc */
+ return "5.2.2";
+
+ case 553: /* Req action not taken: mailbox name not allowed */
+ return "5.1.0";
+
+ case 554: /* Transaction failed */
+ return "5.0.0";
+ }
+
+ if (REPLYTYPE(smtpstat) == 2)
+ return "2.0.0";
+ if (REPLYTYPE(smtpstat) == 4)
+ return "4.0.0";
+ return "5.0.0";
+}
+/*
+** XTEXTIFY -- take regular text and turn it into DSN-style xtext
+**
+** Parameters:
+** t -- the text to convert.
+** taboo -- additional characters that must be encoded.
+**
+** Returns:
+** The xtext-ified version of the same string.
+*/
+
+char *
+xtextify(t, taboo)
+ register char *t;
+ char *taboo;
+{
+ register char *p;
+ int l;
+ int nbogus;
+ static char *bp = NULL;
+ static int bplen = 0;
+
+ if (taboo == NULL)
+ taboo = "";
+
+ /* figure out how long this xtext will have to be */
+ nbogus = l = 0;
+ for (p = t; *p != '\0'; p++)
+ {
+ register int c = (*p & 0xff);
+
+ /* ASCII dependence here -- this is the way the spec words it */
+ if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
+ strchr(taboo, c) != NULL)
+ nbogus++;
+ l++;
+ }
+ if (nbogus < 0)
+ {
+ /* since nbogus is ssize_t and wrapped, 2 * size_t would wrap */
+ syserr("!xtextify string too long");
+ }
+ if (nbogus == 0)
+ return t;
+ l += nbogus * 2 + 1;
+
+ /* now allocate space if necessary for the new string */
+ if (l > bplen)
+ {
+ if (bp != NULL)
+ sm_free(bp); /* XXX */
+ bp = sm_pmalloc_x(l);
+ bplen = l;
+ }
+
+ /* ok, copy the text with byte expansion */
+ for (p = bp; *t != '\0'; )
+ {
+ register int c = (*t++ & 0xff);
+
+ /* ASCII dependence here -- this is the way the spec words it */
+ if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
+ strchr(taboo, c) != NULL)
+ {
+ *p++ = '+';
+ *p++ = "0123456789ABCDEF"[c >> 4];
+ *p++ = "0123456789ABCDEF"[c & 0xf];
+ }
+ else
+ *p++ = c;
+ }
+ *p = '\0';
+ return bp;
+}
+/*
+** XUNTEXTIFY -- take xtext and turn it into plain text
+**
+** Parameters:
+** t -- the xtextified text.
+**
+** Returns:
+** The decoded text. No attempt is made to deal with
+** null strings in the resulting text.
+*/
+
+char *
+xuntextify(t)
+ register char *t;
+{
+ register char *p;
+ int l;
+ static char *bp = NULL;
+ static int bplen = 0;
+
+ /* heuristic -- if no plus sign, just return the input */
+ if (strchr(t, '+') == NULL)
+ return t;
+
+ /* xtext is always longer than decoded text */
+ l = strlen(t);
+ if (l > bplen)
+ {
+ if (bp != NULL)
+ sm_free(bp); /* XXX */
+ bp = xalloc(l);
+ bplen = l;
+ }
+
+ /* ok, copy the text with byte compression */
+ for (p = bp; *t != '\0'; t++)
+ {
+ register int c = *t & 0xff;
+
+ if (c != '+')
+ {
+ *p++ = c;
+ continue;
+ }
+
+ c = *++t & 0xff;
+ if (!isascii(c) || !isxdigit(c))
+ {
+ /* error -- first digit is not hex */
+ usrerr("bogus xtext: +%c", c);
+ t--;
+ continue;
+ }
+ if (isdigit(c))
+ c -= '0';
+ else if (isupper(c))
+ c -= 'A' - 10;
+ else
+ c -= 'a' - 10;
+ *p = c << 4;
+
+ c = *++t & 0xff;
+ if (!isascii(c) || !isxdigit(c))
+ {
+ /* error -- second digit is not hex */
+ usrerr("bogus xtext: +%x%c", *p >> 4, c);
+ t--;
+ continue;
+ }
+ if (isdigit(c))
+ c -= '0';
+ else if (isupper(c))
+ c -= 'A' - 10;
+ else
+ c -= 'a' - 10;
+ *p++ |= c;
+ }
+ *p = '\0';
+ return bp;
+}
+/*
+** XTEXTOK -- check if a string is legal xtext
+**
+** Xtext is used in Delivery Status Notifications. The spec was
+** taken from RFC 1891, ``SMTP Service Extension for Delivery
+** Status Notifications''.
+**
+** Parameters:
+** s -- the string to check.
+**
+** Returns:
+** true -- if 's' is legal xtext.
+** false -- if it has any illegal characters in it.
+*/
+
+bool
+xtextok(s)
+ char *s;
+{
+ int c;
+
+ while ((c = *s++) != '\0')
+ {
+ if (c == '+')
+ {
+ c = *s++;
+ if (!isascii(c) || !isxdigit(c))
+ return false;
+ c = *s++;
+ if (!isascii(c) || !isxdigit(c))
+ return false;
+ }
+ else if (c < '!' || c > '~' || c == '=')
+ return false;
+ }
+ return true;
+}
+/*
+** PRUNEROUTE -- prune an RFC-822 source route
+**
+** Trims down a source route to the last internet-registered hop.
+** This is encouraged by RFC 1123 section 5.3.3.
+**
+** Parameters:
+** addr -- the address
+**
+** Returns:
+** true -- address was modified
+** false -- address could not be pruned
+**
+** Side Effects:
+** modifies addr in-place
+*/
+
+static bool
+pruneroute(addr)
+ char *addr;
+{
+#if NAMED_BIND
+ char *start, *at, *comma;
+ char c;
+ int braclev;
+ int rcode;
+ int i;
+ char hostbuf[BUFSIZ];
+ char *mxhosts[MAXMXHOSTS + 1];
+
+ /* check to see if this is really a route-addr */
+ if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
+ return false;
+
+ /*
+ ** Can't simply find the first ':' is the address might be in the
+ ** form: "<@[IPv6:::1]:user@host>" and the first ':' in inside
+ ** the IPv6 address.
+ */
+
+ start = addr;
+ braclev = 0;
+ while (*start != '\0')
+ {
+ if (*start == ':' && braclev <= 0)
+ break;
+ else if (*start == '[')
+ braclev++;
+ else if (*start == ']' && braclev > 0)
+ braclev--;
+ start++;
+ }
+ if (braclev > 0 || *start != ':')
+ return false;
+
+ at = strrchr(addr, '@');
+ if (at == NULL || at < start)
+ return false;
+
+ /* slice off the angle brackets */
+ i = strlen(at + 1);
+ if (i >= sizeof(hostbuf))
+ return false;
+ (void) sm_strlcpy(hostbuf, at + 1, sizeof(hostbuf));
+ hostbuf[i - 1] = '\0';
+
+ while (start != NULL)
+ {
+ if (getmxrr(hostbuf, mxhosts, NULL, false,
+ &rcode, true, NULL) > 0)
+ {
+ (void) sm_strlcpy(addr + 1, start + 1,
+ strlen(addr) - 1);
+ return true;
+ }
+ c = *start;
+ *start = '\0';
+ comma = strrchr(addr, ',');
+ if (comma != NULL && comma[1] == '@' &&
+ strlen(comma + 2) < sizeof(hostbuf))
+ (void) sm_strlcpy(hostbuf, comma + 2, sizeof(hostbuf));
+ else
+ comma = NULL;
+ *start = c;
+ start = comma;
+ }
+#endif /* NAMED_BIND */
+ return false;
+}
OpenPOWER on IntegriCloud