/*
* 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: envelope.c,v 8.310 2009/12/18 17:08:01 ca Exp $")
/*
** CLRSESSENVELOPE -- clear session oriented data in an envelope
**
** Parameters:
** e -- the envelope to clear.
**
** Returns:
** none.
*/
void
ENVELOPE *e;
{
#if SASL
#endif /* SASL */
#if STARTTLS
# if _FFR_TLS_1
# endif /* _FFR_TLS_1 */
#endif /* STARTTLS */
}
/*
** NEWENVELOPE -- fill in a new envelope
**
** Supports inheritance.
**
** Parameters:
** e -- the new envelope to fill in.
** parent -- the envelope to be the parent of e.
** rpool -- either NULL, or a pointer to a resource pool
** from which envelope memory is allocated, and
** to which envelope resources are attached.
**
** Returns:
** e.
**
** Side Effects:
** none.
*/
ENVELOPE *
register ENVELOPE *e;
{
int sendmode;
/*
** This code used to read:
** if (e == parent && e->e_parent != NULL)
** parent = e->e_parent;
** So if e == parent && e->e_parent == NULL then we would
** set e->e_parent = e, which creates a loop in the e_parent chain.
** This meant macvalue() could go into an infinite loop.
*/
else
if (e == parent)
clearenvelope(e, true, rpool);
if (e == CurEnv)
(char *) &NullAddress,
sizeof(e->e_from));
else
sizeof(e->e_from));
assign_queueid(e);
#if _FFR_SESSID
#endif /* _FFR_SESSID */
{
#if _FFR_SESSID
#endif /* _FFR_SESSID */
{
}
else
{
}
}
return e;
}
/* values for msg_timeout, see also IS_* below for usage (bit layout) */
/* immediate return */
/*
** DROPENVELOPE -- deallocate an envelope.
**
** Parameters:
** e -- the envelope to deallocate.
** fulldrop -- if set, do return receipts.
** split -- if true, split by recipient if message is queued up
**
** Returns:
** EX_* status (currently: 0: success, EX_IOERR on panic)
**
** Side Effects:
** housekeeping necessary to dispose of an envelope.
** Unlocks this queue file.
*/
int
register ENVELOPE *e;
bool fulldrop;
bool split;
{
bool panic = false;
bool queueit = false;
int msg_timeout = 0;
bool failure_return = false;
bool delay_return = false;
bool success_return = false;
bool done = false;
register ADDRESS *q;
{
sm_dprintf("dropenvelope %p: id=", e);
sm_dprintf(", flags=");
printenvflags(e);
{
sm_dprintf("sendq=");
}
}
if (LogLevel > 84)
"dropenvelope, e_flags=0x%lx, OpMode=%c, pid=%d",
/* we must have an id to remove disk files */
return EX_OK;
/* if verify-only mode, we can skip most of this */
goto simpledrop;
sm_dprintf("dropenvelope: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",
e->e_flags &= ~EF_LOGSENDER;
/* post statistics */
/*
** Extract state information from dregs of send list.
*/
if (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 &&
{
}
{
}
e->e_flags &= ~EF_QUEUERUN;
{
if (QS_IS_UNDELIVERED(q->q_state))
queueit = true;
/* see if a notification is needed */
((IS_MSG_ERR(msg_timeout) &&
QS_IS_UNDELIVERED(q->q_state)) ||
QS_IS_BADADDR(q->q_state) ||
{
failure_return = true;
{
&e->e_errorqueue, 0, e);
done = true;
}
}
((QS_IS_SENT(q->q_state) &&
{
success_return = true;
}
}
if (e->e_class < 0)
e->e_flags |= EF_NO_BODY_RETN;
/*
** See if the message timed out.
*/
if (!queueit)
/* EMPTY */
/* nothing to do */ ;
else if (IS_MSG_ERR(msg_timeout))
{
if (failure_return)
{
if (msg_timeout == MSG_NOT_BY)
{
"delivery time expired %lds",
e->e_deliver_by);
}
else
{
"Cannot send message for %s",
false));
}
/* don't free, allocated from e_rpool */
e->e_flags |= EF_CLRQUEUE;
}
if (msg_timeout == MSG_NOT_BY)
{
"Delivery time (%lds) expired\n",
e->e_deliver_by);
}
else
"Message could not be delivered for %s\n",
false));
"Message will be deleted from queue\n");
{
if (QS_IS_UNDELIVERED(q->q_state))
{
q->q_state = QS_BADADDR;
if (msg_timeout == MSG_NOT_BY)
q->q_status = "5.4.7";
else
q->q_status = "4.4.7";
}
}
}
else
{
else if (IS_DLVR_NOTIFY(e) &&
e->e_deliver_by > 0 &&
if (IS_MSG_WARN(msg_timeout))
{
e->e_class >= 0 &&
"-request") != 0))
{
for (q = e->e_sendqueue; q != NULL;
q = q->q_next)
{
if (QS_IS_UNDELIVERED(q->q_state)
#endif /* _FFR_NODELAYDSN_ON_HOLD */
)
{
if (msg_timeout ==
MSG_WARN_BY &&
q->q_flags) ||
q->q_flags))
)
{
delay_return = true;
}
if (bitset(QPINGONDELAY,
q->q_flags))
{
delay_return = true;
}
}
}
}
if (delay_return)
{
if (msg_timeout == MSG_WARN_BY)
{
"Warning: Delivery time (%lds) exceeded",
e->e_deliver_by);
}
else
"Warning: could not send message for past %s",
false));
/* don't free, allocated from e_rpool */
buf);
e->e_flags |= EF_WARNING;
}
if (msg_timeout == MSG_WARN_BY)
{
"Warning: Delivery time (%lds) exceeded\n",
e->e_deliver_by);
}
else
"Warning: message still undelivered after %s\n",
false));
"Will keep trying until message is %s old\n",
false));
}
}
sm_dprintf("failure_return=%d delay_return=%d success_return=%d queueit=%d\n",
/*
** If we had some fatal error, but no addresses are marked as
** bad, mark them _all_ as bad.
*/
{
{
QS_IS_VERIFIED(q->q_state)) &&
{
failure_return = true;
q->q_state = QS_BADADDR;
}
}
}
/*
** Send back return receipts as requested.
*/
{
sm_dprintf("dropenvelope(%s): sending return receipt\n",
id);
e->e_flags |= EF_SENDRECEIPT;
}
e->e_flags &= ~EF_SENDRECEIPT;
/*
** Arrange to send error messages if there are fatal errors.
*/
{
}
/*
** Arrange to send warning messages to postmaster as requested.
*/
if ((failure_return || pmnotify) &&
PostMasterCopy != NULL &&
e->e_class >= 0)
{
if (failure_return)
{
sm_dprintf("dropenvelope(%s): sending postmaster copy to %s\n",
}
if (pmnotify)
&rlist, 0, e);
}
/*
** Instantiate or deinstantiate the queue.
*/
sm_dprintf("dropenvelope(%s): at simpledrop, queueit=%d\n",
{
{
sm_dprintf("\n===== Dropping queue files for %s... queueit=%d, e_flags=",
printenvflags(e);
}
if (!panic)
{
{
}
}
{
/*
** leave the Qf file behind as
** the delivery attempt failed.
*/
/* EMPTY */
}
else
{
/* add to available space in filesystem */
}
e->e_ntries);
}
{
if (!split)
queueup(e, false, true);
else
{
/*
** Save old sibling and set it to NULL to avoid
** queueing up the same envelopes again.
** This requires that envelopes in that list have
** been take care of before (or at some other place).
*/
if (!split_by_recipient(e) &&
{
syserr("!dropenvelope(%s): cannot commit data file %s, uid=%d",
(int) geteuid());
}
queueup(e, false, true);
/* clean up */
{
/* now unlock the job */
sm_dprintf("dropenvelope(%s): unlocking job\n",
/* this envelope is marked unused */
{
}
}
}
}
/* now unlock the job */
closexscript(e);
unlockqueue(e);
/* make sure that this envelope is marked unused */
{
}
if (panic)
return EX_IOERR;
return EX_OK;
}
/*
** CLEARENVELOPE -- clear an envelope without unlocking
**
** This is normally used by a child process to get a clean
** envelope without disturbing the parent.
**
** Parameters:
** e -- the envelope to clear.
** fullclear - if set, the current envelope is total
** garbage and should be ignored; otherwise,
** release any resources it may indicate.
** rpool -- either NULL, or a pointer to a resource pool
** from which envelope memory is allocated, and
** to which envelope resources are attached.
**
** Returns:
** none.
**
** Side Effects:
** Closes files associated with the envelope.
** Marks the envelope as unallocated.
*/
void
register ENVELOPE *e;
bool fullclear;
{
extern ENVELOPE BlankEnvelope;
char **p;
if (!fullclear)
{
/* clear out any file information */
}
/*
** Copy BlankEnvelope into *e.
** It is not safe to simply copy pointers to strings;
** the strings themselves must be copied (or set to NULL).
** The problem is that when we assign a new string value to
** a member of BlankEnvelope, we free the old string.
** We did not need to do this copying in sendmail 8.11 :-(
** and it is a potential performance hit. Reference counted
** strings are one way out.
*/
*e = BlankEnvelope;
e->e_qfletter = '\0';
/*
** Copy the macro table.
** We might be able to avoid this by zeroing the macro table
** and always searching BlankEnvelope.e_macro after e->e_macro
** in macvalue().
*/
++p)
{
if (*p != NULL)
*p = sm_rpool_strdup_x(rpool, *p);
}
/*
** XXX There are many strings in the envelope structure
** XXX that we are not attempting to copy here.
** XXX Investigate this further.
*/
if (Verbose)
{
}
e->e_enhsc[0] = '\0';
#endif /* _FFR_MILTER_ENHSC */
}
/*
** INITSYS -- initialize instantiation of system
**
** In Daemon mode, this is done in the child.
**
** Parameters:
** e -- the envelope to use.
**
** Returns:
** none.
**
** Side Effects:
** Initializes the system macros, some global variables,
** etc. In particular, the current time in various
** forms is set.
*/
void
initsys(e)
register ENVELOPE *e;
{
#ifdef TTYNAME
register char *p;
extern char *ttyname();
#endif /* TTYNAME */
/*
** Give this envelope a reality.
** I.e., an id, a transcript, and a creation time.
** We don't select the queue until all of the recipients are known.
*/
openxscript(e);
e->e_qfletter = '\0';
/*
** Set OutChannel to something useful if stdout isn't it.
** This arranges that any extra stuff the mailer produces
** gets sent back to the user on error (because it is
** tucked away in the transcript).
*/
OutChannel = e->e_xfp;
/*
** Set up some basic system macros.
*/
/* process id */
/* hop count */
/* time as integer, unix time, arpa time */
settime(e);
/* Load average */
sm_getla();
#ifdef TTYNAME
/* tty name */
{
p = ttyname(2);
if (p != NULL)
{
}
}
#endif /* TTYNAME */
}
/*
** SETTIME -- set the current time.
**
** Parameters:
** e -- the envelope in which the macros should be set.
**
** Returns:
** none.
**
** Side Effects:
** Sets the various time macros -- $a, $b, $d, $t.
*/
void
settime(e)
register ENVELOPE *e;
{
register char *p;
if (p != NULL)
*p = '\0';
}
/*
** OPENXSCRIPT -- Open transcript file
**
** Creates a transcript file for possible eventual mailing or
** sending back.
**
** Parameters:
**
** Returns:
** none
**
** Side Effects:
** Creates the transcript file.
*/
#ifndef O_APPEND
# define O_APPEND 0
#endif /* ! O_APPEND */
void
openxscript(e)
register ENVELOPE *e;
{
register char *p;
return;
#if 0
syserr("openxscript: job not locked");
#endif /* 0 */
p = queuename(e, XSCRPT_LETTER);
{
syserr("Can't create transcript file %s", p);
}
{
sm_dprintf("openxscript(%s):\n ", p);
false);
}
}
/*
** CLOSEXSCRIPT -- close the transcript file.
**
** Parameters:
** e -- the envelope containing the transcript to close.
**
** Returns:
** none.
**
** Side Effects:
** none.
*/
void
closexscript(e)
register ENVELOPE *e;
{
return;
#if 0
syserr("closexscript: job not locked");
#endif /* 0 */
}
/*
** SETSENDER -- set the person who this message is from
**
** Under certain circumstances allow the user to say who
** 1. The user's uid is zero (root).
** 2. The user's login name is in an approved list (typically
** from a network server).
** 3. The address the user is trying to claim has a
** "!" character in it (since #2 doesn't do it for
** us if we are dialing out for UUCP).
** A better check to replace #3 would be if the
** effective uid is "UUCP" -- this would require me
** to rewrite getpwent to "grab" uucp as it went by,
** make getname more nasty, do another passwd file
** scan, or compile the UID of "UUCP" into the code,
** all of which are reprehensible.
**
** Assuming all of these fail, we figure out something
** ourselves.
**
** Parameters:
** from -- the person we would like to believe this message
** is from, as specified on the command line.
** e -- the envelope in which we would like the sender set.
** delimptr -- if non-NULL, set to the location of the
** trailing delimiter.
** delimchar -- the character that will delimit the sender
** address.
** internal -- set if this address is coming from an internal
** source such as an owner alias.
**
** Returns:
** none.
**
** Side Effects:
** sets sendmail's notion of who the from person is.
*/
void
char *from;
register ENVELOPE *e;
char **delimptr;
int delimchar;
bool internal;
{
register char **pvp;
char *bp;
extern char *FullName;
/* may be set from earlier calls */
/*
** Figure out the real user executing us.
** Username can return errno != 0 on non-errors.
*/
if (ConfigLevel < 2)
SuprErrs = true;
/* preset state for then clause in case from == NULL */
{
/* log garbage addresses for traceback */
{
char *p;
p = macvalue('_', e);
if (p == NULL)
{
host = MyHostName;
"%.*s@%.*s", MAXNAME,
p = ebuf;
}
"setsender: %s: invalid or unparsable, received from %s",
}
{
{
/* it was a bogus mailer in the from addr */
e->e_status = "5.1.7";
"553 Invalid sender address");
}
SuprErrs = true;
}
{
SuprErrs = true;
syserr("553 5.3.0 setsender: can't even parse postmaster!");
}
}
else
FromFlag = true;
{
sm_dprintf("setsender: QS_SENDER ");
}
SuprErrs = false;
#if USERDB
{
register char *p;
if (p != NULL)
from = p;
}
#endif /* USERDB */
{
if (!internal)
{
/* if the user already given fullname don't redefine */
{
if (FullName[0] == '\0')
else
}
}
{
/*
** Process passwd file entry.
*/
/* extract home directory */
else
/* extract user and group id */
{
}
/* extract full name from passwd file */
{
}
}
else
{
}
}
{
{
{
}
}
}
/*
** Rewrite the from person to dispose of possible implicit
** links in the net.
*/
IntTokenTab, false);
{
/* don't need to give error -- prescan did that already */
if (LogLevel > 2)
"cannot prescan from (%s)",
}
{
/* heuristic: route-addr: add angle brackets */
*--bp = '<';
}
/* save the domain spec if this mailer wants it */
{
char **lastat;
/* get rid of any pesky angle brackets */
/* strip off to the last "@" sign */
{
}
{
{
sm_dprintf("Saving from domain: ");
}
}
}
}
/*
** PRINTENVFLAGS -- print envelope flags for debugging
**
** Parameters:
** e -- the envelope with the flags to be printed.
**
** Returns:
** none.
*/
struct eflags
{
char *ef_name;
unsigned long ef_bit;
};
{
{ "OLDSTYLE", EF_OLDSTYLE },
{ "INQUEUE", EF_INQUEUE },
{ "NO_BODY_RETN", EF_NO_BODY_RETN },
{ "CLRQUEUE", EF_CLRQUEUE },
{ "SENDRECEIPT", EF_SENDRECEIPT },
{ "FATALERRS", EF_FATALERRS },
{ "DELETE_BCC", EF_DELETE_BCC },
{ "RESPONSE", EF_RESPONSE },
{ "RESENT", EF_RESENT },
{ "VRFYONLY", EF_VRFYONLY },
{ "WARNING", EF_WARNING },
{ "QUEUERUN", EF_QUEUERUN },
{ "GLOBALERRS", EF_GLOBALERRS },
{ "PM_NOTIFY", EF_PM_NOTIFY },
{ "METOO", EF_METOO },
{ "LOGSENDER", EF_LOGSENDER },
{ "NORECEIPT", EF_NORECEIPT },
{ "HAS8BIT", EF_HAS8BIT },
{ "NL_NOT_EOL", EF_NL_NOT_EOL },
{ "CRLF_NOT_EOL", EF_CRLF_NOT_EOL },
{ "RET_PARAM", EF_RET_PARAM },
{ "HAS_DF", EF_HAS_DF },
{ "IS_MIME", EF_IS_MIME },
{ "DONT_MIME", EF_DONT_MIME },
{ "DISCARD", EF_DISCARD },
{ "TOOBIG", EF_TOOBIG },
{ "SPLIT", EF_SPLIT },
{ "UNSAFE", EF_UNSAFE },
{ NULL, 0 }
};
void
register ENVELOPE *e;
{
bool first = true;
{
continue;
if (first)
else
first = false;
}
if (!first)
sm_dprintf(">\n");
}