* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)envelope.c 8.43 (Berkeley) %G%";
** NEWENVELOPE -- allocate a new envelope
** e -- the new envelope to fill in.
** parent -- the envelope to be the parent of e.
register ENVELOPE
*parent
;
extern putheader(), putbody();
extern ENVELOPE BlankEnvelope
;
if (e
== parent
&& e
->e_parent
!= NULL
)
bcopy((char *) &NullAddress
, (char *) &e
->e_from
, sizeof e
->e_from
);
bcopy((char *) &CurEnv
->e_from
, (char *) &e
->e_from
, sizeof e
->e_from
);
e
->e_msgpriority
= parent
->e_msgsize
;
if (CurEnv
->e_xfp
!= NULL
)
(void) fflush(CurEnv
->e_xfp
);
** DROPENVELOPE -- deallocate an envelope.
** e -- the envelope to deallocate.
** housekeeping necessary to dispose of an envelope.
** Unlocks this queue file.
bool failure_return
= FALSE
;
bool success_return
= FALSE
;
bool return_no
, return_yes
;
printf("dropenvelope %x: id=", e
);
printf(", flags=0x%x\n", e
->e_flags
);
printaddr(e
->e_sendqueue
, TRUE
);
/* we must have an id to remove disk files */
if (LogLevel
> 4 && bitset(EF_LOGSENDER
, e
->e_flags
))
syslog(LOG_DEBUG
, "dropenvelope, id=%s, flags=0x%x, pid=%d",
id
, e
->e_flags
, getpid());
e
->e_flags
&= ~EF_LOGSENDER
;
** Extract state information from dregs of send list.
e
->e_flags
&= ~EF_QUEUERUN
;
return_no
= return_yes
= FALSE
;
for (q
= e
->e_sendqueue
; q
!= NULL
; q
= q
->q_next
)
if (bitset(QQUEUEUP
, q
->q_flags
))
/* see if a notification is needed */
if (bitset(QBADADDR
, q
->q_flags
) &&
bitset(QPINGONFAILURE
, q
->q_flags
))
if (q
->q_owner
== NULL
&& !emptyaddr(&e
->e_from
))
(void) sendtolist(e
->e_from
.q_paddr
, NULL
,
else if (bitset(QSENT
, q
->q_flags
) &&
bitnset(M_LOCALMAILER
, q
->q_mailer
->m_flags
) &&
bitset(QPINGONSUCCESS
, q
->q_flags
))
/* common code for error returns and return receipts */
/* test for returning the body */
if (!bitset(QHASRETPARAM
, q
->q_flags
))
if (!bitset(EF_NORETURN
, e
->e_flags
))
else if (bitset(QNOBODYRETURN
, q
->q_flags
))
if (return_no
&& !return_yes
)
e
->e_flags
|= EF_NORETURN
;
** See if the message timed out.
else if (curtime() > e
->e_ctime
+ TimeOuts
.to_q_return
[e
->e_timeoutclass
])
(void) sprintf(buf
, "Cannot send message for %s",
pintvl(TimeOuts
.to_q_return
[e
->e_timeoutclass
], FALSE
));
if (e
->e_message
!= NULL
)
e
->e_message
= newstr(buf
);
e
->e_flags
|= EF_CLRQUEUE
;
fprintf(e
->e_xfp
, "Message could not be delivered for %s\n",
pintvl(TimeOuts
.to_q_return
[e
->e_timeoutclass
], FALSE
));
fprintf(e
->e_xfp
, "Message will be deleted from queue\n");
for (q
= e
->e_sendqueue
; q
!= NULL
; q
= q
->q_next
)
if (bitset(QQUEUEUP
, q
->q_flags
))
else if (TimeOuts
.to_q_warning
[e
->e_timeoutclass
] > 0 &&
curtime() > e
->e_ctime
+ TimeOuts
.to_q_warning
[e
->e_timeoutclass
])
if (!bitset(EF_WARNING
|EF_RESPONSE
, e
->e_flags
) &&
strcmp(e
->e_from
.q_paddr
, "<>") != 0 &&
strncasecmp(e
->e_from
.q_paddr
, "owner-", 6) != 0 &&
(strlen(e
->e_from
.q_paddr
) <= 8 ||
strcasecmp(&e
->e_from
.q_paddr
[strlen(e
->e_from
.q_paddr
) - 8], "-request") != 0))
"Warning: cannot send message for %s",
pintvl(TimeOuts
.to_q_warning
[e
->e_timeoutclass
], FALSE
));
if (e
->e_message
!= NULL
)
e
->e_message
= newstr(buf
);
e
->e_flags
|= EF_WARNING
;
"Warning: message still undelivered after %s\n",
pintvl(TimeOuts
.to_q_warning
[e
->e_timeoutclass
], FALSE
));
fprintf(e
->e_xfp
, "Will keep trying until message is %s old\n",
pintvl(TimeOuts
.to_q_return
[e
->e_timeoutclass
], FALSE
));
for (q
= e
->e_sendqueue
; q
!= NULL
; q
= q
->q_next
)
if (bitset(QQUEUEUP
, q
->q_flags
))
printf("failure_return=%d success_return=%d queueit=%d\n",
failure_return
, success_return
, queueit
);
** Send back return receipts as requested.
if (e->e_receiptto != NULL && bitset(EF_SENDRECEIPT, e->e_flags)
&& !bitset(PRIV_NORECEIPTS, PrivacyFlags))
if (e
->e_receiptto
== NULL
)
e
->e_receiptto
= e
->e_from
.q_paddr
;
if (success_return
&& strcmp(e
->e_receiptto
, "<>") != 0)
auto ADDRESS
*rlist
= NULL
;
e
->e_flags
|= EF_SENDRECEIPT
;
(void) sendtolist(e
->e_receiptto
, NULLADDR
, &rlist
, e
);
(void) returntosender("Return receipt", rlist
, FALSE
, e
);
e
->e_flags
&= ~EF_SENDRECEIPT
;
** Arrange to send error messages if there are fatal errors.
if (failure_return
&& e
->e_errormode
!= EM_QUIET
)
** Arrange to send warning messages to postmaster as requested.
if (bitset(EF_PM_NOTIFY
, e
->e_flags
) && PostMasterCopy
!= NULL
&&
!bitset(EF_RESPONSE
, e
->e_flags
) && e
->e_class
>= 0)
auto ADDRESS
*rlist
= NULL
;
(void) sendtolist(PostMasterCopy
, NULLADDR
, &rlist
, e
);
(void) returntosender(e
->e_message
, rlist
, FALSE
, e
);
** Instantiate or deinstantiate the queue.
if ((!queueit
&& !bitset(EF_KEEPQUEUE
, e
->e_flags
)) ||
bitset(EF_CLRQUEUE
, e
->e_flags
))
printf("\n===== Dropping [dq]f%s (queueit=%d, e_flags=%x) =====\n\n",
e
->e_id
, queueit
, e
->e_flags
);
xunlink(queuename(e
, 'q'));
syslog(LOG_INFO
, "%s: done", id
);
else if (queueit
|| !bitset(EF_INQUEUE
, e
->e_flags
))
queueup(e
, bitset(EF_KEEPQUEUE
, e
->e_flags
), FALSE
);
syserr("554 dropenvelope: queueup");
/* make sure that this envelope is marked unused */
(void) xfclose(e
->e_dfp
, "dropenvelope", e
->e_df
);
e
->e_id
= e
->e_df
= NULL
;
** CLEARENVELOPE -- clear an envelope without unlocking
** This is normally used by a child process to get a clean
** envelope without disturbing the parent.
** 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.
** Closes files associated with the envelope.
** Marks the envelope as unallocated.
clearenvelope(e
, fullclear
)
extern ENVELOPE BlankEnvelope
;
/* clear out any file information */
(void) xfclose(e
->e_xfp
, "clearenvelope xfp", e
->e_id
);
(void) xfclose(e
->e_dfp
, "clearenvelope dfp", e
->e_df
);
e
->e_xfp
= e
->e_dfp
= NULL
;
/* now clear out the data */
STRUCTCOPY(BlankEnvelope
, *e
);
e
->e_sendmode
= SM_DELIVER
;
bh
= BlankEnvelope
.e_header
;
*nhp
= (HDR
*) xalloc(sizeof *bh
);
bcopy((char *) bh
, (char *) *nhp
, sizeof *bh
);
** INITSYS -- initialize instantiation of system
** In Daemon mode, this is done in the child.
** Initializes the system macros, some global variables,
** etc. In particular, the current time in various
char cbuf
[5]; /* holds hop count */
char pbuf
[10]; /* holds pid */
static char ybuf
[60]; /* holds tty id */
** Give this envelope a reality.
** I.e., an id, a transcript, and a creation time.
** 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).
if (OpMode
== MD_DAEMON
&& bitset(EF_QUEUERUN
, e
->e_flags
) &&
** Set up some basic system macros.
(void) sprintf(pbuf
, "%d", getpid());
define('p', newstr(pbuf
), e
);
(void) sprintf(cbuf
, "%d", e
->e_hopcount
);
define('c', newstr(cbuf
), e
);
/* time as integer, unix time, arpa time */
if (macvalue('y', e
) == NULL
)
if (strrchr(p
, '/') != NULL
)
** SETTIME -- set the current time.
** Sets the various time macros -- $a, $b, $d, $t.
char tbuf
[20]; /* holds "current" time */
char dbuf
[30]; /* holds ctime(tbuf) */
extern struct tm
*gmtime();
(void) sprintf(tbuf
, "%04d%02d%02d%02d%02d", tm
->tm_year
+ 1900,
tm
->tm_mon
+1, tm
->tm_mday
, tm
->tm_hour
, tm
->tm_min
);
define('t', newstr(tbuf
), e
);
(void) strcpy(dbuf
, ctime(&now
));
define('d', newstr(dbuf
), e
);
if (macvalue('a', e
) == NULL
)
** OPENXSCRIPT -- Open transcript file
** Creates a transcript file for possible eventual mailing or
** e -- the envelope to create the transcript in/for.
** Creates the transcript file.
fd
= open(p
, O_WRONLY
|O_CREAT
|O_APPEND
, 0644);
syserr("Can't create transcript file %s", p
);
fd
= open("/dev/null", O_WRONLY
, 0644);
syserr("!Can't open /dev/null");
e
->e_xfp
= fdopen(fd
, "w");
syserr("!Can't create transcript stream %s", p
);
printf("openxscript(%s):\n ", p
);
dumpfd(fileno(e
->e_xfp
), TRUE
, FALSE
);
** CLOSEXSCRIPT -- close the transcript file.
** e -- the envelope containing the transcript to close.
(void) xfclose(e
->e_xfp
, "closexscript", e
->e_id
);
** SETSENDER -- set the person who this message is from
** Under certain circumstances allow the user to say who
** s/he is (using -f or -r). These are:
** 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
** 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
** internal -- set if this address is coming from an internal
** source such as an owner alias.
** sets sendmail's notion of who the from person is.
setsender(from
, e
, delimptr
, internal
)
register struct passwd
*pw
;
extern struct passwd
*getpwnam();
printf("setsender(%s)\n", from
== NULL
? "" : from
);
** Figure out the real user executing us.
** Username can return errno != 0 on non-errors.
if (bitset(EF_QUEUERUN
, e
->e_flags
) || OpMode
== MD_SMTP
||
OpMode
== MD_ARPAFTP
|| OpMode
== MD_DAEMON
)
if (realname
== NULL
|| realname
[0] == '\0')
delimchar
= internal
? '\0' : ' ';
e
->e_from
.q_flags
= QBADADDR
;
parseaddr(from
, &e
->e_from
, RF_COPYALL
|RF_SENDERADDR
,
delimchar
, delimptr
, e
) == NULL
||
bitset(QBADADDR
, e
->e_from
.q_flags
) ||
e
->e_from
.q_mailer
== ProgMailer
||
e
->e_from
.q_mailer
== FileMailer
||
e
->e_from
.q_mailer
== InclMailer
)
/* log garbage addresses for traceback */
if (from
!= NULL
&& LogLevel
> 2)
char ebuf
[MAXNAME
* 2 + 2];
char *host
= RealHostName
;
(void) sprintf(ebuf
, "%s@%s", realname
, host
);
"setsender: %s: invalid or unparseable, received from %s",
shortenstring(from
, 83), p
);
if (!bitset(QBADADDR
, e
->e_from
.q_flags
))
/* it was a bogus mailer in the from addr */
usrerr("553 Invalid sender address");
parseaddr(from
= newstr(realname
), &e
->e_from
,
RF_COPYALL
|RF_SENDERADDR
, ' ', NULL
, e
) == NULL
)
expand("\201n", nbuf
, &nbuf
[sizeof nbuf
], e
);
if (parseaddr(from
= newstr(nbuf
), &e
->e_from
,
RF_COPYALL
, ' ', NULL
, e
) == NULL
&&
parseaddr(from
= "postmaster", &e
->e_from
,
RF_COPYALL
, ' ', NULL
, e
) == NULL
)
syserr("553 setsender: can't even parse postmaster!");
e
->e_from
.q_flags
|= QDONTSEND
;
printf("setsender: QDONTSEND ");
printaddr(&e
->e_from
, FALSE
);
if (bitnset(M_CHECKUDB
, e
->e_from
.q_mailer
->m_flags
))
extern char *udbsender();
p
= udbsender(e
->e_from
.q_user
);
if (bitnset(M_HASPWENT
, e
->e_from
.q_mailer
->m_flags
))
/* if the user already given fullname don't redefine */
FullName
= macvalue('x', e
);
if (FullName
!= NULL
&& FullName
[0] == '\0')
if ((pw
= getpwnam(e
->e_from
.q_user
)) != NULL
)
** Process passwd file entry.
/* extract home directory */
if (strcmp(pw
->pw_dir
, "/") == 0)
e
->e_from
.q_home
= newstr("");
e
->e_from
.q_home
= newstr(pw
->pw_dir
);
define('z', e
->e_from
.q_home
, e
);
/* extract user and group id */
e
->e_from
.q_uid
= pw
->pw_uid
;
e
->e_from
.q_gid
= pw
->pw_gid
;
e
->e_from
.q_flags
|= QGOODUID
;
/* extract full name from passwd file */
if (FullName
== NULL
&& pw
->pw_gecos
!= NULL
&&
strcmp(pw
->pw_name
, e
->e_from
.q_user
) == 0 &&
if (buildfname(pw
->pw_gecos
, e
->e_from
.q_user
, buf
) &&
hvalue("MIME-Version", e
) == NULL
)
addheader("MIME-Version", "1.0", e
);
if (FullName
!= NULL
&& !internal
)
define('x', FullName
, e
);
else if (!internal
&& OpMode
!= MD_DAEMON
)
if (e
->e_from
.q_home
== NULL
)
e
->e_from
.q_home
= getenv("HOME");
if (e
->e_from
.q_home
!= NULL
&&
strcmp(e
->e_from
.q_home
, "/") == 0)
e
->e_from
.q_uid
= RealUid
;
e
->e_from
.q_gid
= RealGid
;
e
->e_from
.q_flags
|= QGOODUID
;
** Rewrite the from person to dispose of possible implicit
pvp
= prescan(from
, delimchar
, pvpbuf
, sizeof pvpbuf
, NULL
);
/* don't need to give error -- prescan did that already */
syslog(LOG_NOTICE
, "cannot prescan from (%s)", from
);
(void) rewrite(pvp, 3, 0, e);
(void) rewrite(pvp, 1, 0, e);
(void) rewrite(pvp, 4, 0, e);
cataddr(pvp
, NULL
, bp
, sizeof buf
- 2, '\0');
if (*bp
== '@' && !bitnset(M_NOBRACKET
, e
->e_from
.q_mailer
->m_flags
))
/* heuristic: route-addr: add angle brackets */
e
->e_sender
= newstr(bp
);
define('f', e
->e_sender
, e
);
/* save the domain spec if this mailer wants it */
if (e
->e_from
.q_mailer
!= NULL
&&
bitnset(M_CANONICAL
, e
->e_from
.q_mailer
->m_flags
))
extern char **copyplist();
while (*pvp
!= NULL
&& strcmp(*pvp
, "@") != 0)
e
->e_fromdomain
= copyplist(pvp
, TRUE
);