a49b2281123cfcce568f764b499e5244c008db08
static char SccsId
[] = "@(#)deliver.c 3.52 %G%";
** DELIVER -- Deliver a message to a list of addresses.
** This routine delivers to everyone on the same host as the
** user on the head of the list. It is clever about mailers
** that don't handle multiple users. It is NOT guaranteed
** that it will deliver to all these addresses however -- so
** deliver should be called once for each address on the
** firstto -- head of the address list to deliver to.
** editfcn -- if non-NULL, we want to call this function
** to output the letter (instead of just out-
** zero -- successfully delivered.
** else -- some failure, see ExitStat for more info.
** The standard input is passed off to someone.
deliver(firstto
, editfcn
)
char *host
; /* host being sent to */
char *user
; /* user being sent to */
register struct mailer
*m
; /* mailer for this recipient */
extern bool checkcompat();
char tobuf
[MAXLINE
]; /* text line of to people */
extern ADDRESS
*getctladdr();
char tfrombuf
[MAXNAME
]; /* translated from person */
register ADDRESS
*to
= firstto
;
if (!ForceMail
&& bitset(QDONTSEND
, to
->q_flags
))
printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n",
to
->q_mailer
->m_mno
, to
->q_host
, to
->q_user
);
** Do initial argv setup.
** Insert the mailer name. Notice that $x expansion is
** NOT done on the mailer name. Then, if the mailer has
** a picky -f flag, we insert it as appropriate. This
** code does not check for 'pv' overflow; this places a
** manifest lower limit of 4 for MAXPV.
/* rewrite from address, using rewriting rules */
(void) expand(m
->m_from
, buf
, &buf
[sizeof buf
- 1]);
mvp
= prescan(buf
, '\0');
syserr("bad mailer from translate \"%s\"", buf
);
cataddr(mvp
, tfrombuf
, sizeof tfrombuf
);
define('g', tfrombuf
); /* translated sender address */
define('h', host
); /* to host */
/* insert -f or -r flag as appropriate */
if (bitset(M_FOPT
|M_ROPT
, m
->m_flags
) && FromFlag
)
if (bitset(M_FOPT
, m
->m_flags
))
(void) expand("$g", buf
, &buf
[sizeof buf
- 1]);
** Append the other fixed parts of the argv. These run
** up to the first entry containing "$u". There can only
** be one of these, and there are only a few more slots
for (mvp
= m
->m_argv
; (p
= *++mvp
) != NULL
; )
while ((p
= index(p
, '$')) != NULL
)
/* this entry is safe -- go ahead and process it */
(void) expand(*mvp
, buf
, &buf
[sizeof buf
- 1]);
if (pvp
>= &pv
[MAXPV
- 3])
syserr("Too many parameters to %s before $u", pv
[0]);
syserr("No $u in mailer argv for %s", pv
[0]);
** At this point *mvp points to the argument with $u. We
** run through our address list and append all the addresses
** we can. If we run out of space, do not fret! We can
** always send another copy later.
for (; to
!= NULL
; to
= to
->q_next
)
/* avoid sending multiple recipients to dumb mailers */
if (tobuf
[0] != '\0' && !bitset(M_MUSER
, m
->m_flags
))
/* if already sent or not for this host, don't send */
if ((!ForceMail
&& bitset(QDONTSEND
, to
->q_flags
)) ||
strcmp(to
->q_host
, host
) != 0)
/* compute effective uid/gid when sending */
if (to
->q_mailer
== ProgMailer
)
ctladdr
= getctladdr(to
);
to
->q_flags
|= QDONTSEND
;
printf(" send to `%s'\n", user
);
** Check to see that these people are allowed to
giveresponse(EX_UNAVAILABLE
, TRUE
, m
);
** Strip quote bits from names if the mailer is dumb
if (bitset(M_STRIPQ
, m
->m_flags
))
stripquotes(user
, FALSE
);
stripquotes(host
, FALSE
);
** If an error message has already been given, don't
** bother to send to this address.
** >>>>>>>>>> This clause assumes that the local mailer
** >> NOTE >> cannot do any further aliasing; that
** >>>>>>>>>> function is subsumed by sendmail.
if (bitset(QBADADDR
, to
->q_flags
))
/* save statistics.... */
Stat
.stat_nt
[to
->q_mailer
->m_mno
]++;
Stat
.stat_bt
[to
->q_mailer
->m_mno
] += kbytes(MsgSize
);
** See if this user name is "special".
** If the user name has a slash in it, assume that this
** is a file -- send it off without further ado.
** Note that this means that editfcn's will not
** be applied to the message. Also note that
** this type of addresses is not processed along
** with the others, so we fudge on the To person.
if (index(user
, '/') != NULL
)
i
= mailfile(user
, getctladdr(to
));
giveresponse(i
, TRUE
, m
);
** Address is verified -- add this user to mailer
** argv, and add it to the print list of recipients.
/* create list of users for error messages */
(void) strcat(tobuf
, ",");
(void) strcat(tobuf
, to
->q_paddr
);
define('u', user
); /* to user */
define('z', to
->q_home
); /* user's home */
/* expand out this user */
(void) expand(*mvp
, buf
, &buf
[sizeof buf
- 1]);
if (pvp
>= &pv
[MAXPV
- 2])
/* allow some space for trailing parms */
/* see if any addresses still exist */
/* print out messages as full list */
** Fill out any parameters after the $u parameter.
(void) expand(*mvp
, buf
, &buf
[sizeof buf
- 1]);
syserr("deliver: pv overflow after $u for %s", pv
[0]);
** The argument vector gets built, pipes
** are created as necessary, and we fork & exec as
i
= sendoff(m
, pv
, editfcn
, ctladdr
);
** If we got a temporary failure, arrange to queue the
for (to
= firstto
; to
!= NULL
; to
= to
->q_next
)
if (bitset(QBADADDR
, to
->q_flags
))
** DOFORK -- do a fork, retrying a couple of times on failure.
** This MUST be a macro, since after a vfork we are running
** two processes on the same stack!!!
** From a macro??? You've got to be kidding!
** Modifies the ==> LOCAL <== variable 'pid', leaving:
** pid of child in parent, zero in child.
** -1 on unrecoverable error.
** I'm awfully sorry this looks so awful. That's
# define DOFORK(fORKfN) \
for (i = NFORKTRIES; i-- > 0; )\
sleep((unsigned) NFORKTRIES - i);\
** DOFORK -- simple fork interface to DOFORK.
** pid of child in parent.
** returns twice, once in parent and once in child.
** SENDOFF -- send off call to mailer & collect response.
** m -- mailer descriptor.
** pvp -- parameter vector to send to it.
** editfcn -- function to pipe it through.
** ctladdr -- an address pointer controlling the
** user/groupid etc. of the mailer.
** exit status of mailer.
sendoff(m
, pvp
, editfcn
, ctladdr
)
/* create a pipe to shove the mail through */
/* pid is set by DOFORK */
/* child -- set up input & exec mailer */
/* make diagnostic output be standard output */
(void) signal(SIGINT
, SIG_IGN
);
(void) signal(SIGHUP
, SIG_IGN
);
(void) signal(SIGTERM
, SIG_DFL
);
/* arrange to filter standard & diag output of command */
if (OutChannel
!= stdout
)
(void) dup(fileno(OutChannel
));
/* arrange to get standard input */
syserr("Cannot dup to zero!");
if (!bitset(M_RESTR
, m
->m_flags
))
extern int DefUid
, DefGid
;
(void) setgid(ctladdr
->q_gid
);
(void) setuid(ctladdr
->q_uid
);
** We have to be careful with vfork - we can't mung up the
** memory but we don't want the mailer to inherit any extra
** open files. Chances are the mailer won't
** care about an extra file, but then again you never know.
** Actually, we would like to close(fileno(pwf)), but it's
** declared static so we can't. But if we fclose(pwf), which
** is what endpwent does, it closes it in the parent too and
** the next getpwnam will be slower. If you have a weird
** mailer that chokes on the extra file you should do the
** Similar comments apply to log. However, openlog is
** clever enough to set the FIOCLEX mode on the file,
** so it will be closed automatically on the exec.
/* syserr fails because log is closed */
/* syserr("Cannot exec %s", m->m_mailer); */
printf("Cannot exec '%s' errno=%d\n", m
->m_mailer
, errno
);
** Format and write message to mailer.
(void) signal(SIGPIPE
, SIG_IGN
);
mfile
= fdopen(mpvect
[1], "w");
** Wait for child to die and report status.
** We should never get fatal errors (e.g., segmentation
** violation), so we report those specially. For other
** errors, we choose a status message (into statmsg),
** and if it represents an error, we print it.
while ((i
= wait(&st
)) > 0 && i
!= pid
)
syserr("%s: stat %o", pvp
[0], st
);
ExitStat
= EX_UNAVAILABLE
;
giveresponse(i
, TRUE
, m
);
** GIVERESPONSE -- Interpret an error response from a mailer
** stat -- the status code from the mailer (high byte
** only; core dumps must have been taken care of
** force -- if set, force an error message output, even
** if the mailer seems to like to print its own
** m -- the mailer descriptor for this mailer.
** Errors may be incremented.
giveresponse(stat
, force
, m
)
register struct mailer
*m
;
** Compute status message from code.
if (i
< 0 || i
> N_SysEx
)
if (bitset(M_LOCAL
, m
->m_flags
))
message(Arpa_Info
, statmsg
);
else if (stat
== EX_TEMPFAIL
)
message(Arpa_Info
, "transmission deferred");
if (statmsg
== NULL
&& m
->m_badstat
!= 0)
if (i
< 0 || i
>= N_SysEx
)
syserr("Bad m_badstat %d", stat
);
usrerr("unknown mailer response %d", stat
);
else if (force
|| !bitset(M_QUIET
, m
->m_flags
) || Verbose
)
** Log a record of the transaction. Compute the new
** ExitStat -- if we already had an error, stick with
(void) sprintf(buf
, "error %d", stat
);
syslog(LOG_INFO
, "%s->%s: %ld: %s", From
.q_paddr
, To
, MsgSize
, statmsg
);
** PUTMESSAGE -- output a message to the final mailer.
** then passes the rest of the message through. If we have
** managed to extract a date already, use that; otherwise,
** use the current date/time.
** fp -- file to output onto.
** m -- a mailer descriptor.
** The message is written onto fp.
extern char *capitalize();
** Output "From" line unless supressed
if (!bitset(M_NHDR
, m
->m_flags
))
(void) expand("$l", buf
, &buf
[sizeof buf
- 1]);
fprintf(fp
, "%s\n", buf
);
** Output all header lines
of_line
= hvalue("original-from");
for (h
= Header
; h
!= NULL
; h
= h
->h_link
)
char *origfrom
= OrigFrom
;
if (bitset(H_CHECK
|H_ACHECK
, h
->h_flags
) && !bitset(h
->h_mflags
, m
->m_flags
))
p
= ")><("; /* can't happen (I hope) */
/* use From: line from message if generated is the same */
if (strcmp(h
->h_field
, "from") == 0 && origfrom
!= NULL
&&
strcmp(m
->m_from
, "$f") == 0 && of_line
== NULL
)
else if (bitset(H_DEFAULT
, h
->h_flags
))
(void) expand(h
->h_value
, buf
, &buf
[sizeof buf
]);
if (p
== NULL
|| *p
== '\0')
/* hack, hack -- output Original-From field if different */
if (strcmp(h
->h_field
, "from") == 0 && origfrom
!= NULL
)
/* output new Original-From line if needed */
if (of_line
== NULL
&& !samefrom(p
, origfrom
))
fprintf(fp
, "Original-From: %s\n", origfrom
);
if (of_line
!= NULL
&& !nooutput
&& samefrom(p
, of_line
))
/* delete Original-From: line if redundant */
else if (strcmp(h
->h_field
, "original-from") == 0 && of_line
== NULL
)
/* finally, output the header line */
fprintf(fp
, "%s: %s\n", capitalize(h
->h_field
), p
);
** Output the body of the message
while (!ferror(fp
) && (i
= fread(buf
, 1, BUFSIZ
, TempFile
)) > 0)
(void) fwrite(buf
, 1, i
, fp
);
syserr("putmessage: read error");
if (ferror(fp
) && errno
!= EPIPE
)
syserr("putmessage: write error");
** SAMEFROM -- tell if two text addresses represent the same from address.
** ifrom -- internally generated form of from address.
** efrom -- external form of from address.
** TRUE -- if they convey the same info.
** FALSE -- if any information has been lost.
printf("samefrom(%s,%s)-->", ifrom
, efrom
);
if (strcmp(ifrom
, efrom
) == 0)
if (strcmp(buf
, efrom
) == 0)
** MAILFILE -- Send a message to a file.
** If the file has the setuid/setgid bits set, but NO execute
** bits, sendmail will try to become the owner of that file
** rather than the real user. Obviously, this only works if
** sendmail runs as root.
** filename -- the name of the file to send to.
** ctladdr -- the controlling address header -- includes
** the userid/groupid to be when sending.
** The exit code associated with the operation.
mailfile(filename
, ctladdr
)
** Fork so we can change permissions here.
** Note that we MUST use fork, not vfork, because of
** the complications of calling subroutines, etc.
/* child -- actually write to file */
extern int DefUid
, DefGid
;
(void) signal(SIGINT
, SIG_DFL
);
(void) signal(SIGHUP
, SIG_DFL
);
(void) signal(SIGTERM
, SIG_DFL
);
if (stat(filename
, &stb
) < 0)
if (bitset(0111, stb
.st_mode
))
if (!bitset(S_ISGID
, stb
.st_mode
) || setgid(stb
.st_gid
) < 0)
(void) setgid(ctladdr
->q_gid
);
if (!bitset(S_ISUID
, stb
.st_mode
) || setuid(stb
.st_uid
) < 0)
(void) setuid(ctladdr
->q_uid
);
f
= fopen(filename
, "a");
putmessage(f
, Mailer
[1]);
/* reset ISUID & ISGID bits */
(void) chmod(filename
, (int) stb
.st_mode
);
/* parent -- wait for exit status */
while ((i
= wait(&stat
)) != pid
)
stat
= EX_UNAVAILABLE
<< 8;
return ((stat
>> 8) & 0377);
** SENDALL -- actually send all the messages.
** verifyonly -- if set, only give verification messages.
** Scans the send lists and sends everything it finds.
for (i
= 0; Mailer
[i
] != NULL
; i
++)
for (q
= Mailer
[i
]->m_sendq
; q
!= NULL
; q
= q
->q_next
)
if (!bitset(QDONTSEND
|QBADADDR
, q
->q_flags
))
if (bitset(M_LOCAL
, q
->q_mailer
->m_flags
))
message(Arpa_Info
, "deliverable");
message(Arpa_Info
, "queueable");
(void) deliver(q
, (fnptr
) NULL
);