* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988 Regents of the University of California.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)recipient.c 5.18 (Berkeley) %G%";
** SENDTOLIST -- Designate a send list.
** The parameter is a comma-separated list of people to send to.
** This routine arranges to send to all of them.
** The `ctladdr' is the address that expanded to be this one,
** e.g., in an alias expansion. This is used for a number of
** purposed, most notably inheritance of uid/gid for protection
** purposes. It is also used to detect self-reference in group
** expansions and the like.
** list -- the send list.
** ctladdr -- the address template for the person to
** send to -- effective uid/gid are important.
** This is typically the alias that caused this
** sendq -- a pointer to the head of a queue to put
** qflags -- special flags to set in the q_flags field.
** pointer to chain of addresses.
sendto(list
, copyf
, ctladdr
, qflags
)
register ADDRESS
*al
; /* list of addresses to send to */
bool firstone
; /* set on first address sent */
bool selfref
; /* set if this list includes ctladdr */
char delimiter
; /* the address delimiter */
ADDRESS
*sibl
; /* sibling pointer in tree */
ADDRESS
*prev
; /* previous sibling */
printf("sendto: %s\n ctladdr=", list
);
printaddr(ctladdr
, FALSE
);
/* heuristic to determine old versus new style addresses */
(index(list
, ',') != NULL
|| index(list
, ';') != NULL
||
index(list
, '<') != NULL
|| index(list
, '(') != NULL
))
CurEnv
->e_flags
&= ~EF_OLDSTYLE
;
if (!bitset(EF_OLDSTYLE
, CurEnv
->e_flags
) || ctladdr
!= NULL
)
for (p
= list
; *p
!= '\0'; )
extern char *DelimChar
; /* defined in prescan */
while (isspace(*p
) || *p
== ',')
a
= parseaddr(p
, (ADDRESS
*) NULL
, 1, delimiter
);
a
->q_flags
|= ctladdr
->q_flags
& ~QPRIMARY
;
/* see if this should be marked as a primary address */
(firstone
&& *p
== '\0' && bitset(QPRIMARY
, ctladdr
->q_flags
)))
/* put on send queue or suppress self-reference */
if (ctladdr
!= NULL
&& sameaddr(ctladdr
, a
))
/* if this alias doesn't include itself, delete ctladdr */
if (!selfref
&& ctladdr
!= NULL
)
ctladdr
->q_flags
|= QDONTSEND
;
/* arrange to send to everyone on the local send list */
register ADDRESS
*a
= al
;
extern ADDRESS
*recipient();
extern ADDRESS
*recipient();
extern ADDRESS
*addrref();
if (sibl
->q_fullname
== NULL
&& ctladdr
!= NULL
)
sibl
->q_fullname
= ctladdr
->q_fullname
;
/* link tree together (but only if the node is new) */
** ADDRREF -- return pointer to address that references another address.
** a -- address to check.
** r -- reference to find.
** address of node in tree rooted at 'a' that references
** NULL if no such node exists.
if (a
->q_child
== r
|| a
->q_sibling
== r
)
q
= addrref(a
->q_child
, r
);
** RECIPIENT -- Designate a message recipient
** Saves the named person for future mailing.
** a -- the (preparsed) address header for the recipient.
** sendq -- a pointer to the head of a queue to put the
** recipient in. Duplicate supression is done
** pointer to address actually inserted in send list.
register ADDRESS
**sendq
;
register struct mailer
*m
;
bool quoted
= FALSE
; /* set if the addr has a quote bit */
char buf
[MAXNAME
]; /* unquoted image of the user name */
extern ADDRESS
*getctladdr();
CurEnv
->e_to
= a
->q_paddr
;
/* break aliasing loops */
if (AliasLevel
> MAXRCRSN
)
usrerr("aliasing/forwarding loop broken");
** Finish setting up address structure.
/* set the queue timeout */
/* map user & host to lower case if requested on non-aliases */
/* get unquoted user for file, program or user.name check */
(void) strcpy(buf
, a
->q_user
);
for (p
= buf
; *p
!= '\0' && !quoted
; p
++)
if (!isascii(*p
) && (*p
& 0377) != (SpaceSub
& 0377))
/* do sickly crude mapping for program mailing, etc. */
if (m
== LocalMailer
&& buf
[0] == '|')
a
->q_mailer
= m
= ProgMailer
;
if (a
->q_alias
== NULL
&& !QueueRun
&& !ForceMail
)
a
->q_flags
|= QDONTSEND
|QBADADDR
;
usrerr("Cannot mail directly to programs");
** Look up this person in the recipient list.
** If they are there already, return, otherwise continue.
** If the list is empty, just add it. Notice the cute
** hack to make from addresses suppress things correctly:
** the QDONTSEND bit will be set in the send list.
** [Please note: the emphasis is on "hack."]
for (pq
= sendq
; (q
= *pq
) != NULL
; pq
= &q
->q_next
)
if (!ForceMail
&& sameaddr(q
, a
))
printf("%s in sendq: ", a
->q_paddr
);
if (Verbose
&& !bitset(QDONTSEND
|QPSEUDO
, a
->q_flags
))
message(Arpa_Info
, "duplicate suppressed");
if (!bitset(QPRIMARY
, q
->q_flags
))
q
->q_flags
|= a
->q_flags
;
if (!bitset(QPSEUDO
, a
->q_flags
))
/* add address on list */
** Alias the name and handle :include: specs.
if (m
== LocalMailer
&& !bitset(QDONTSEND
, a
->q_flags
))
if (strncmp(a
->q_user
, ":include:", 9) == 0)
if (a
->q_alias
== NULL
&& !QueueRun
&& !ForceMail
)
usrerr("Cannot mail directly to :include:s");
message(Arpa_Info
, "including file %s", &a
->q_user
[9]);
include(&a
->q_user
[9], " sending", a
, sendq
);
** If the user is local and still being sent, verify that
** the address is good. If it is, try to forward.
** If the address is already good, we have a forwarding
** loop. This can be broken by just sending directly to
** the user (which is probably correct anyway).
if (!bitset(QDONTSEND
, a
->q_flags
) && m
== LocalMailer
)
/* see if this is to a file */
/* check if writable or creatable */
if (a
->q_alias
== NULL
&& !QueueRun
&& !ForceMail
)
a
->q_flags
|= QDONTSEND
|QBADADDR
;
usrerr("Cannot mail directly to files");
else if ((stat(buf
, &stb
) >= 0) ? (!writable(&stb
)) :
(*p
= '\0', !safefile(buf
, getruid(), S_IWRITE
|S_IEXEC
)))
giveresponse(EX_CANTCREAT
, m
, CurEnv
);
register struct passwd
*pw
;
extern struct passwd
*finduser();
/* warning -- finduser may trash buf */
giveresponse(EX_NOUSER
, m
, CurEnv
);
if (strcmp(a
->q_user
, pw
->pw_name
) != 0)
a
->q_user
= newstr(pw
->pw_name
);
(void) strcpy(buf
, pw
->pw_name
);
a
->q_home
= newstr(pw
->pw_dir
);
buildfname(pw
->pw_gecos
, pw
->pw_name
, nbuf
);
a
->q_fullname
= newstr(nbuf
);
a
->q_fullname
= newstr(nbuf
);
** FINDUSER -- find the password entry for a user.
** This looks a lot like getpwnam, except that it may want to
** do some fancier pattern matching in /etc/passwd.
** This routine contains most of the time of many sendmail runs.
** It deserves to be optimized.
** name -- the name to match against.
** A pointer to a pw struct.
** NULL if name is unknown or ambiguous.
register struct passwd
*pw
;
extern struct passwd
*getpwent();
extern struct passwd
*getpwnam();
/* map upper => lower case */
for (p
= name
; *p
!= '\0'; p
++)
if (isascii(*p
) && isupper(*p
))
/* look up this login name using fast path */
if ((pw
= getpwnam(name
)) != NULL
)
/* search for a matching full name instead */
for (p
= name
; *p
!= '\0'; p
++)
if (*p
== (SpaceSub
& 0177) || *p
== '_')
while ((pw
= getpwent()) != NULL
)
if (index(buf
, ' ') != NULL
&& !strcasecmp(buf
, name
))
message(Arpa_Info
, "sending to %s <%s>",
** WRITABLE -- predicate returning if the file is writable.
** This routine must duplicate the algorithm in sys/fio.c.
** Unfortunately, we cannot use the access call since we
** won't necessarily be the real uid when we try to
** actually open the file.
** Notice that ANY file with ANY execute bit is automatically
** not writable. This is also enforced by mailfile.
** s -- pointer to a stat struct for the file.
** TRUE -- if we will be able to write this file.
** FALSE -- if we cannot write this file.
if (bitset(0111, s
->st_mode
))
if (bitset(S_ISUID
, s
->st_mode
))
if (bitset(S_ISGID
, s
->st_mode
))
return ((s
->st_mode
& bits
) != 0);
** INCLUDE -- handle :include: specification.
** fname -- filename to include.
** msg -- message to print in verbose mode.
** ctladdr -- address template to use to fill in these
** addresses -- effective user/group id are
** sendq -- a pointer to the head of the send queue
** to put these addresses in.
** reads the :include: file and sends to everyone
include(fname
, msg
, ctladdr
, sendq
)
char *oldto
= CurEnv
->e_to
;
char *oldfilename
= FileName
;
int oldlinenumber
= LineNumber
;
usrerr("Cannot open %s", fname
);
if (getctladdr(ctladdr
) == NULL
)
if (fstat(fileno(fp
), &st
) < 0)
syserr("Cannot fstat %s!", fname
);
ctladdr
->q_uid
= st
.st_uid
;
ctladdr
->q_gid
= st
.st_gid
;
ctladdr
->q_flags
|= QGOODUID
;
/* read the file -- each line is a comma-separated list. */
while (fgets(buf
, sizeof buf
, fp
) != NULL
)
register char *p
= index(buf
, '\n');
message(Arpa_Info
, "%s to %s", msg
, buf
);
sendto(buf
, 1, ctladdr
, 0);
LineNumber
= oldlinenumber
;
** SENDTOARGV -- send to an argument vector.
** argv -- argument vector to send to.
** puts all addresses on the argument vector onto the
while ((p
= *argv
++) != NULL
)
if (argv
[0] != NULL
&& argv
[1] != NULL
&& !strcasecmp(argv
[0], "at"))
if (strlen(p
) + strlen(argv
[1]) + 2 > sizeof nbuf
)
usrerr("address overflow");
(void) strcat(nbuf
, "@");
(void) strcat(nbuf
, argv
[1]);
sendto(p
, 0, (ADDRESS
*) NULL
, 0);
** GETCTLADDR -- get controlling address from an address header.
** If none, get one corresponding to the effective userid.
** a -- the address to find the controller of.
** the controlling address.
while (a
!= NULL
&& !bitset(QGOODUID
, a
->q_flags
))