⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 savemail.c

📁 < linux网络编程工具>>配套源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/*
 * Copyright (c) 1998-2000 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.
 *
 */

#ifndef lint
static char id[] = "@(#)$Id: savemail.c,v 8.212.4.5 2000/08/22 22:46:00 gshapiro Exp $";
#endif /* ! lint */

#include <sendmail.h>


static void	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:
**		none
**
**	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 */


void
savemail(e, sendbody)
	register ENVELOPE *e;
	bool sendbody;
{
	register struct passwd *pw;
	register FILE *fp;
	int state;
	auto ADDRESS *q = NULL;
	register char *p;
	MCI mcibuf;
	int flags;
	long sff;
	char buf[MAXLINE + 1];

	if (tTd(6, 1))
	{
		dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n  e_from=",
			e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
			ExitStat);
		printaddr(&e->e_from, FALSE);
	}

	if (e->e_id == NULL)
	{
		/* can't return a message with no id */
		return;
	}

	/*
	**  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) == NULL)
		{
			syserr("553 5.3.5 Cannot parse Postmaster!");
			finis(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;

	  default:
		syserr("554 5.3.0 savemail: bogus errormode x%x\n",
		       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;
		}
		state = ESM_POSTMASTER;
	}

	while (state != ESM_DONE)
	{
		if (tTd(6, 5))
			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).
			*/

			p = ttypath();
			if (p == NULL || freopen(p, "w", stdout) == NULL)
			{
				state = ESM_MAIL;
				break;
			}

			expand("\201n", buf, sizeof buf, e);
			printf("\r\nMessage from %s...\r\n", buf);
			printf("Errors occurred while sending mail.\r\n");
			if (e->e_xfp != NULL)
			{
				(void) bfrewind(e->e_xfp);
				printf("Transcript follows:\r\n");
				while (fgets(buf, sizeof buf, e->e_xfp) != NULL &&
				       !ferror(stdout))
					(void) fputs(buf, stdout);
			}
			else
			{
				syserr("Cannot open %s", queuename(e, 'x'));
				printf("Transcript of session is unavailable.\r\n");
			}
			printf("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 (strlen(e->e_from.q_paddr) >= sizeof from)
				{
					state = ESM_POSTMASTER;
					break;
				}
				(void) strlcpy(from, e->e_from.q_paddr,
					       sizeof from);

				if (!DontPruneRoutes && pruneroute(from))
				{
					ADDRESS *a;

					for (a = e->e_errorqueue; a != NULL;
					     a = a->q_next)
					{
						if (sameaddr(a, &e->e_from))
							a->q_state = QS_DUPLICATE;
					}
				}
				(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);
			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 ((pw = sm_getpwnam(e->e_from.q_user)) != NULL &&
					 *pw->pw_dir != '\0')
					p = pw->pw_dir;
			}
			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 */
			define('z', p, e);

			/* get the sender for the UnixFromLine */
			p = macvalue('g', e);
			define('g', e->e_sender, e);

			expand("\201z/dead.letter", buf, sizeof buf, e);
			sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
			if (RealUid == 0)
				sff |= SFF_ROOTOK;
			e->e_to = buf;
			if (writable(buf, NULL, sff) &&
			    mailfile(buf, 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", buf);
				Verbose = oldverb;
				define('g', p, e);
				state = ESM_DONE;
				break;
			}
			define('g', p, e);
			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);
			define('g', e->e_sender, e);

			putfromline(&mcibuf, e);
			(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);
			(*e->e_putbody)(&mcibuf, e, NULL);
			putline("\n", &mcibuf);
			(void) fflush(fp);
			if (ferror(fp) ||
			    fclose(fp) < 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;
			}
			define('g', p, e);
			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");
			errno = 0;
			syserr("!554 savemail: cannot save rejected email anywhere");
		}
	}
}
/*
**  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	100	/* 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))
	{
		dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%lx, returnq=",
			msg, returndepth, (u_long) e);
		printaddr(returnq, TRUE);
		if (tTd(6, 20))
		{
			dprintf("Sendq=");
			printaddr(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;
	}

	define('g', e->e_sender, e);
	define('u', NULL, e);

	/* initialize error envelope */
	ee = newenvelope(&errenvelope, e);
	define('a', "\201b", ee);
	define('r', "", ee);
	define('s', "localhost", ee);
	define('_', "localhost", ee);
#if SASL
	define(macid("{auth_type}", NULL), "", ee);
	define(macid("{auth_authen}", NULL), "", ee);
	define(macid("{auth_author}", NULL), "", ee);
	define(macid("{auth_ssf}", NULL), "", ee);
#endif /* SASL */
#if STARTTLS
	define(macid("{cert_issuer}", NULL), "", ee);
	define(macid("{cert_subject}", NULL), "", ee);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -