/*
* Copyright (c) 1998-2003, 2006 Proofpoint, 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: recipient.c,v 8.351 2013-11-22 20:51:56 ca Exp $")
static void includetimeout __P((int));
/*
** SORTHOST -- strcmp()-like func for host portion of an ADDRESS
**
** Parameters:
** xx -- first ADDRESS
** yy -- second ADDRESS
**
** Returns:
** <0 when xx->q_host is less than yy->q_host
** >0 when xx->q_host is greater than yy->q_host
** 0 when equal
*/
static int
{
/* XXX maybe compare hostnames from the end? */
#else /* _FFR_HOST_SORT_REVERSE */
#endif /* _FFR_HOST_SORT_REVERSE */
}
/*
** SORTEXPENSIVE -- strcmp()-like func for expensive mailers
**
** The mailer has been noted already as "expensive" for 'xx'. This
** will give a result relative to 'yy'. Expensive mailers get rated
** "greater than" non-expensive mailers because during the delivery phase
** it will get queued -- no use it getting in the way of less expensive
** recipients. We avoid an MX RR lookup when both 'xx' and 'yy' are
** expensive since an MX RR lookup happens when extracted from the queue
** later.
**
** Parameters:
** xx -- first ADDRESS
** yy -- second ADDRESS
**
** Returns:
** <0 when xx->q_host is less than yy->q_host and both are
** expensive
** >0 when xx->q_host is greater than yy->q_host, or when
** 'yy' is non-expensive
** 0 when equal (by expense and q_host)
*/
static int
{
return 1; /* xx should go later */
/* XXX maybe compare hostnames from the end? */
#else /* _FFR_HOST_SORT_REVERSE */
#endif /* _FFR_HOST_SORT_REVERSE */
}
/*
** SORTBYSIGNATURE -- a strcmp()-like func for q_mailer and q_host in ADDRESS
**
** Parameters:
** xx -- first ADDRESS
** yy -- second ADDRESS
**
** Returns:
** 0 when the "signature"'s are same
** <0 when xx->q_signature is less than yy->q_signature
** >0 when xx->q_signature is greater than yy->q_signature
**
** Side Effect:
** May set ADDRESS pointer for q_signature if not already set.
*/
static int
{
register int ret;
/* Let's avoid redoing the signature over and over again */
/*
** If the two signatures are the same then we will return a sort
** value based on 'q_user'. But note that we have reversed xx and yy
** on purpose. This additional compare helps reduce the number of
** sameaddr() calls and loops in recipient() for the case when
** the rcpt list has been provided already in-order.
*/
if (ret == 0)
else
return ret;
}
/*
** 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.
**
** Parameters:
** list -- the send list.
** ctladdr -- the address template for the person to
** This is typically the alias that caused this
** expansion.
** sendq -- a pointer to the head of a queue to put
** these people into.
** aliaslevel -- the current alias nesting depth -- to
** diagnose loops.
** e -- the envelope in which to add these recipients.
**
** Returns:
** The number of addresses actually on the list.
*/
/* q_flags bits inherited from ctladdr */
int
char *list;
int aliaslevel;
register ENVELOPE *e;
{
register char *p;
SM_NONVOLATILE int i;
char *endp;
{
syserr("sendtolist: null list");
return 0;
}
{
}
/* heuristic to determine old versus new style addresses */
e->e_flags &= ~EF_OLDSTYLE;
delimiter = ' ';
delimiter = ',';
naddrs = 0;
/* make sure we have enough space to copy the string */
if (i <= sizeof(buf))
{
i = sizeof(buf);
}
else
bufp = sm_malloc_x(i);
{
for (p = bufp; *p != '\0'; )
{
auto char *delimptr;
register ADDRESS *a;
/* parse the address */
p++;
&delimptr, e, true);
p = delimptr;
if (a == NULL)
continue;
/* arrange to inherit attributes from parent */
{
ADDRESS *b;
/* self reference test */
{
{
sm_dprintf("sendtolist: QSELFREF ");
}
}
/* check for address loops */
b = self_reference(a);
if (b != NULL)
{
{
sm_dprintf("sendtolist: QSELFREF ");
printaddr(sm_debug_file(), b, false);
}
if (a != b)
{
{
sm_dprintf("sendtolist: QS_DONTSEND ");
printaddr(sm_debug_file(), a, false);
}
a->q_state = QS_DONTSEND;
continue;
}
}
/* full name */
if (a->q_fullname == NULL)
/* various flag bits */
a->q_flags &= ~QINHERITEDBITS;
/* DSN recipient information */
}
al = a;
}
/* arrange to send to everyone on the local send list */
{
naddrs++;
}
}
{
}
return naddrs;
}
#if MILTER
/*
** REMOVEFROMLIST -- Remove addresses from a send list.
**
** The parameter is a comma-separated list of recipients to remove.
** Note that it only deletes matching addresses. If those addresses
** have been expanded already in the sendq, it won't mark the
** expanded recipients as QS_REMOVED.
**
** Parameters:
** list -- the list to remove.
** sendq -- a pointer to the head of a queue to remove
** these addresses from.
** e -- the envelope in which to remove these recipients.
**
** Returns:
** The number of addresses removed from the list.
**
*/
int
char *list;
ENVELOPE *e;
{
SM_NONVOLATILE int i;
char *p;
{
syserr("removefromlist: null list");
return 0;
}
/* heuristic to determine old versus new style addresses */
e->e_flags &= ~EF_OLDSTYLE;
delimiter = ' ';
delimiter = ',';
naddrs = 0;
/* make sure we have enough space to copy the string */
if (i <= sizeof(buf))
{
i = sizeof(buf);
}
else
bufp = sm_malloc_x(i);
{
if (AddrTypeModes)
"e r d");
else
#endif /* _FFR_ADDR_TYPE_MODES */
for (p = bufp; *p != '\0'; )
{
ADDRESS a; /* parsed address to be removed */
ADDRESS *q;
char *delimptr;
/* parse the address */
p++;
{
p = delimptr;
continue;
}
p = delimptr;
{
if (!QS_IS_DEAD(q->q_state) &&
(sameaddr(q, &a) ||
{
{
sm_dprintf("removefromlist: QS_REMOVED ");
printaddr(sm_debug_file(), &a, false);
}
q->q_state = QS_REMOVED;
naddrs++;
break;
}
}
}
}
{
}
return naddrs;
}
#endif /* MILTER */
/*
** RECIPIENT -- Designate a message recipient
** Saves the named person for future mailing (after some checks).
**
** Parameters:
** new -- the (preparsed) address header for the recipient.
** sendq -- a pointer to the head of a queue to put the
** recipient in. Duplicate suppression is done
** in this queue.
** aliaslevel -- the current alias nesting depth.
** e -- the current envelope.
**
** Returns:
** The actual address in the queue. This will be "a" if
** the address is not a duplicate, else the original address.
**
*/
ADDRESS *
int aliaslevel;
register ENVELOPE *e;
{
register ADDRESS *q;
register struct mailer *m;
register char *p;
int i, buflen;
bool insert;
int findusercount;
bool initialdontsend;
char *buf;
p = NULL;
quoted = false;
insert = false;
findusercount = 0;
errno = 0;
if (aliaslevel == 0)
{
}
/* if this is primary, use it as original recipient */
{
if (e->e_origrcpt == NULL)
e->e_origrcpt = "";
}
/* find parent recipient for finalrcpt and orcpt */
continue;
/* find final recipient DSN address */
{
if (p == NULL)
p = "rfc822";
if (sm_strcasecmp(p, "rfc822") != 0)
{
q->q_mailer->m_addrtype,
q->q_user);
}
{
p, q->q_user);
}
{
char *qp;
bool b;
/* strip brackets from address */
b = false;
if (*qp == '<')
{
if (b)
qp++;
}
p, qp);
/* undo damage */
if (b)
}
else
{
"%s; %.700s@%.100s",
p, q->q_user, MyHostName);
}
}
#if _FFR_GEN_ORCPT
/* set ORCPT DSN arg if not already set */
{
/* check for an existing ORCPT */
else
{
/* make our own */
bool b = false;
char *qp;
if (p == NULL)
p = "rfc822";
/* FFR: Needs to strip comments from stdin addrs */
/* strip brackets from address */
if (*qp == '<')
{
if (b)
qp++;
}
{
/* if too big, don't use it */
obuf[0] = '\0';
}
/* undo damage */
if (b)
if (obuf[0] != '\0')
}
}
#endif /* _FFR_GEN_ORCPT */
/* break aliasing loops */
if (aliaslevel > MaxAliasRecursion)
{
{
}
{
"aliasing/forwarding loop broken: %s (%d aliases deep; %d max)",
}
"554 aliasing/forwarding loop broken (%d aliases deep; %d max)",
return new;
}
/*
** Finish setting up address structure.
*/
/* get unquoted user for file, program or user.name check */
if (i >= sizeof(buf0))
{
buflen = i + 1;
}
else
{
}
{
if (*p == '\\')
quoted = true;
}
/* check for direct mailing to restricted mailers */
if (m == ProgMailer)
{
{
"550 Cannot mail directly to programs");
}
{
"550 UID %d is an unknown user: cannot mail to programs",
else
"550 User %s@%s doesn't have a valid shell for mailing to programs",
}
{
"550 Address %s is unsafe for mailing 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 QS_DUPLICATE state will be set in the send list.
** [Please note: the emphasis is on "hack."]
*/
/*
** If this message is going to the queue or FastSplit is set
** and it is the first try and the envelope hasn't split, then we
** avoid doing an MX RR lookup now because one will be done when the
** message is extracted from the queue later. It can go to the queue
** because all messages are going to the queue or this mailer of
** the current recipient is marked expensive.
*/
FastSplit > 0))
else
{
/*
** If address is "less than" it should be inserted now.
** If address is "greater than" current comparison it'll
** insert later in the list; so loop again (if possible).
** If address is "equal" (different equal than sameaddr()
** call) then check if sameaddr() will be true.
** Because this list is now sorted, it'll mean fewer
** comparisons and fewer loops which is important for more
** recipients.
*/
if (i == 0) /* equal */
{
/*
** Sortbysignature() has said that the two have
** equal MX RR's and the same user. Calling sameaddr()
** now checks if the two hosts are as identical as the
** MX RR's are (which might not be the case)
** before saying these are the identical addresses.
*/
{
{
sm_dprintf("%s in sendq: ",
printaddr(sm_debug_file(), q, false);
}
{
message("duplicate suppressed");
else
q->q_state = QS_DUPLICATE;
}
|| q->q_state == QS_REMOVED)
{
/*
** If an earlier milter removed the
** address, a later one can still add
** it back.
*/
}
new = q;
goto done;
}
}
else if (i < 0) /* less than */
{
insert = true;
break;
}
}
/* pq should point to an address, never NULL */
/* add address on list */
if (insert)
{
/*
** insert before 'pq'. Only possible when at least 1
** ADDRESS is in the list already.
*/
else
}
else
{
/*
** Place in list at current 'pq' position. Possible
** when there are 0 or more ADDRESS's in the list.
*/
}
/* added a new address: clear split flag */
/*
** Alias the name and handle special mailer types.
*/
{
sm_dprintf("at trylocaluser: ");
}
{
e->e_nrcpts++;
goto testselfdestruct;
}
if (m == InclMailer)
{
{
"550 Cannot mail directly to :include:s");
}
else
{
int ret;
sendq, aliaslevel, e);
if (transienterror(ret))
{
if (LogLevel > 2)
"include %s: transient error: %s",
sm_errstring(ret));
usrerr("451 4.2.4 Cannot open %s: %s",
sm_errstring(ret));
}
else if (ret != 0)
{
"550 Cannot open %s: %s",
sm_errstring(ret));
}
}
}
else if (m == FileMailer)
{
/* check if allowed */
{
"550 Cannot mail directly to files");
}
{
"550 UID %d is an unknown user: cannot mail to files",
else
"550 User %s@%s doesn't have a valid shell for mailing to files",
}
{
"550 Address %s is unsafe for mailing to files",
}
}
/* try aliasing */
#if USERDB
/* if not aliased, look it up in the user database */
{
{
"Deferred: user database error");
if (LogLevel > 8)
"deferred: udbexpand: %s",
message("queued (user database error): %s",
e->e_nrcpts++;
goto testselfdestruct;
}
}
#endif /* USERDB */
/*
** If we have a level two config file, then pass the name through
** Ruleset 5 before sending it off. Ruleset 5 has the right
** to rewrite it to another mailer. This gives us a hook
** after local aliasing has been done.
*/
{
sm_dprintf("recipient: testing local? cl=%d, rr5=%p\n\t",
}
{
}
/*
** If it didn't get rewritten to another mailer, go ahead
** and deliver it.
*/
{
auto bool fuzzy;
int status;
/* warning -- finduser may trash buf */
switch (status)
{
case EX_TEMPFAIL:
break;
default:
break;
case EX_OK:
if (fuzzy)
{
/* name was a fuzzy match */
if (findusercount++ > 3)
{
"554 aliasing/forwarding loop for %s broken",
goto done;
}
/* see if it aliases */
goto trylocaluser;
}
else
{
}
{
}
{
/* don't do any more now */
}
else if (!quoted)
}
}
e->e_nrcpts++;
{
sm_dprintf("testselfdestruct: ");
{
sm_dprintf("SENDQ:\n");
sm_dprintf("----\n");
}
}
{
{
if (!QS_IS_DEAD(q->q_state))
break;
}
if (q == NULL)
{
"554 aliasing/forwarding loop broken");
}
}
done:
/*
** If we are at the top level, check to see if this has
** expanded to exactly one address. If so, it can inherit
** the primaryness of the address.
**
** While we're at it, clear the QTHISPASS bits.
*/
if (aliaslevel == 0)
{
int nrcpts = 0;
{
QS_IS_SENDABLE(q->q_state))
{
nrcpts++;
only = q;
}
}
if (nrcpts == 1)
{
/* check to see if this actually got a new owner */
q = only;
{
break;
}
if (q == NULL)
}
else if (!initialdontsend && nrcpts > 0)
{
/* arrange for return receipt */
e->e_flags |= EF_SENDRECEIPT;
"%s... expanded to multiple addresses\n",
}
}
return new;
}
/*
** FINDUSER -- find the password entry for a user.
**
** This looks a lot like getpwnam, except that it may want to
**
** This routine contains most of the time of many sendmail runs.
** It deserves to be optimized.
**
** Parameters:
** name -- the name to match against.
** fuzzyp -- an outarg that is set to true if this entry
** was found using the fuzzy matching algorithm;
** set to false otherwise.
** user -- structure to fill in if user is found
**
** Returns:
** On success, fill in *user, set *fuzzyp and return EX_OK.
** If the user was not found, return EX_NOUSER.
** On error, return EX_TEMPFAIL or EX_OSERR.
**
** Side Effects:
** may modify name.
*/
int
char *name;
bool *fuzzyp;
{
#if MATCHGECOS
#endif /* MATCHGECOS */
register char *p;
bool tryagain;
int status;
*fuzzyp = false;
#if HESIOD
/* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */
for (p = name; *p != '\0'; p++)
break;
if (*p == '\0')
{
sm_dprintf("failed (numeric input)\n");
return EX_NOUSER;
}
#endif /* HESIOD */
/* look up this login name using fast path */
{
return status;
}
/* try mapping it to lower case */
tryagain = false;
for (p = name; *p != '\0'; p++)
{
{
*p = tolower(*p);
tryagain = true;
}
}
{
*fuzzyp = true;
return status;
}
#if MATCHGECOS
/* see if fuzzy matching allowed */
if (!MatchGecos)
{
sm_dprintf("not found (fuzzy disabled)\n");
return EX_NOUSER;
}
/* search for a matching full name instead */
for (p = name; *p != '\0'; p++)
{
*p = ' ';
}
(void) setpwent();
{
# if 0
{
sm_dprintf("found (case wrapped)\n");
break;
}
# endif /* 0 */
{
break;
}
}
*fuzzyp = true;
sm_dprintf("no fuzzy match found\n");
# if DEC_OSF_BROKEN_GETPWENT /* DEC OSF/1 3.2 or earlier */
endpwent();
# endif /* DEC_OSF_BROKEN_GETPWENT */
return EX_NOUSER;
return EX_OK;
#else /* MATCHGECOS */
sm_dprintf("not found (fuzzy disabled)\n");
return EX_NOUSER;
#endif /* MATCHGECOS */
}
/*
** WRITABLE -- predicate returning if the file is writable.
**
** 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.
**
** Parameters:
** filename -- the file name to check.
** ctladdr -- the controlling address for this file.
** flags -- SFF_* flags to control the function.
**
** Returns:
** true -- if we will be able to write this file.
** false -- if we cannot write this file.
**
** Side Effects:
** none.
*/
bool
char *filename;
long flags;
{
/*
** File does exist -- check that it is writable.
*/
if (geteuid() != 0)
{
}
{
}
{
user = RealUserName;
}
{
{
}
else
{
}
else
}
else
{
}
{
if (euid == 0)
{
}
if (egid == 0)
}
if (geteuid() == 0 &&
flags |= SFF_SETUIDOK;
flags |= SFF_NOSLINK;
flags |= SFF_NOHLINK;
return errno == 0;
}
/*
** INCLUDE -- handle :include: specification.
**
** Parameters:
** fname -- filename to include.
** forwarding -- if true, we are reading a .forward file.
** if false, it's a :include: file.
** ctladdr -- address template to use to fill in these
** the important things.
** sendq -- a pointer to the head of the send queue
** to put these addresses in.
** aliaslevel -- the alias nesting depth.
** e -- the current envelope.
**
** Returns:
** open error status
**
** Side Effects:
** reads the :include: file and sends to everyone
** listed in that file.
**
** Security Note:
** If you have restricted chown (that is, you can't
** give a file away), it is reasonable to allow programs
** and files called from this :include: file to be to be
** run as the owner of the :include: file. This is bogus
** if there is any chance of someone giving away a file.
** We assume that pre-POSIX systems can give away files.
**
** There is an additional restriction that if you
** forward to a :include: file, it will not take on
** the ownership of the :include: file. This may not
** be necessary, but shouldn't hurt.
*/
int
char *fname;
bool forwarding;
int aliaslevel;
ENVELOPE *e;
{
int nincludes;
int mode;
volatile bool maxreached = false;
char *volatile user;
int rval = 0;
register char *p;
bool safechown = false;
volatile bool safedir = false;
sm_dprintf(" ruid=%d euid=%d\n",
{
sm_dprintf("ctladdr ");
}
sm_dprintf("include: old uid = %d/%d\n",
if (forwarding)
{
sfflags |= SFF_NOGWFILES;
sfflags |= SFF_NOWWFILES;
}
else
{
sfflags |= SFF_NOGWFILES;
sfflags |= SFF_NOWWFILES;
}
/*
** If RunAsUser set, won't be able to run programs as user
** so mark them as unsafe unless the administrator knows better.
*/
{
sm_dprintf("include: not safe (euid=%d, RunAsUid=%d)\n",
}
{
}
else
{
}
#if MAILER_SETUID_METHOD != USE_SETUID
if (saveduid == 0)
{
if (!DontInitGroups)
{
{
syserr("include: initgroups(%s, %d) failed",
goto resetuid;
}
}
else
{
{
syserr("include: setgroups() failed");
goto resetuid;
}
}
{
goto resetuid;
}
if (uid != 0)
{
# if MAILER_SETUID_METHOD == USE_SETEUID
{
syserr("seteuid(%d) failure (real=%d, eff=%d)",
goto resetuid;
}
# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
# if MAILER_SETUID_METHOD == USE_SETREUID
{
syserr("setreuid(0, %d) failure (real=%d, eff=%d)",
goto resetuid;
}
# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
}
}
#endif /* MAILER_SETUID_METHOD != USE_SETUID */
sm_dprintf("include: new uid = %d/%d\n",
/*
** If home directory is remote mounted but server is down,
** this can hang or give errors; use a timeout to avoid this
*/
if (setjmp(CtxIncludeTimeout) != 0)
{
errno = 0;
/* return pseudo-error code */
goto resetuid;
}
if (TimeOuts.to_fileopen > 0)
else
/* check for writable parent directory */
if (p != NULL)
{
int ret;
*p = '\0';
sfflags|SFF_SAFEDIRPATH, 0, 0);
if (ret == 0)
{
/* in safe directory: relax chown & link rules */
safedir = true;
}
else
{
if (bitnset((forwarding ?
else if (bitnset((forwarding ?
ret == E_SM_GWDIR)
{
0, 0);
if (ret == 0)
else
}
else
if (ret > E_PSEUDOBASE &&
!bitnset((forwarding ?
{
if (LogLevel > 11)
"%s: unsafe directory path, marked unsafe",
}
}
*p = '/';
}
/* allow links only in unwritable directories */
if (!safedir &&
!bitnset((forwarding ?
sfflags |= SFF_NOLINK;
if (rval != 0)
{
/* don't use this :include: file */
sm_dprintf("include: not safe (uid=%d): %s\n",
}
{
}
{
sm_dprintf("include: file changed after open\n");
}
#if HASSETREUID || USESETEUID
if (saveduid == 0)
{
if (uid != 0)
{
# if USESETEUID
if (seteuid(0) < 0)
syserr("!seteuid(0) failure (real=%d, eff=%d)",
# else /* USESETEUID */
if (setreuid(-1, 0) < 0)
syserr("!setreuid(-1, 0) failure (real=%d, eff=%d)",
syserr("!setreuid(%d, 0) failure (real=%d, eff=%d)",
(int) geteuid());
# endif /* USESETEUID */
}
syserr("!setgid(%d) failure (real=%d eff=%d)",
(int) getegid());
}
#endif /* HASSETREUID || USESETEUID */
sm_dprintf("include: reset uid = %d/%d\n",
if (rval == E_SM_OPENTIMEOUT)
return rval;
{
return rval;
}
/* if path was writable, check to avoid file giveaway tricks */
sm_dprintf("include: parent of %s is %s, chown is %ssafe\n",
/* if no controlling user or coming from an alias delivery */
if (safechown &&
{
}
{
/* optimization -- avoid getpwuid if we already have info */
}
else if (!forwarding)
{
{
}
else
{
char *sh;
if (safechown)
else
{
if (LogLevel > 11)
"%s: user %s has bad shell %s, marked %s",
if (safechown)
else
}
}
}
{
/* don't do any more now */
e->e_nrcpts++;
return rval;
}
/*
** Check to see if some bad guy can write this file
**
** Group write checking could be more clever, e.g.,
** guessing as to which groups are actually safe ("sys"
** may be; "user" probably is not).
*/
if (!bitnset((forwarding ?
{
sm_dprintf("include: %s is %s writable, marked unsafe\n",
: "group");
if (LogLevel > 11)
"%s: %s writable %s file, marked unsafe",
}
/* read the file -- each line is a comma-separated list. */
LineNumber = 0;
nincludes = 0;
{
LineNumber++;
continue;
/* <sp>#@# introduces a comment anywhere */
/* for Japanese character sets */
{
if (p[1] == '@' && p[2] == '#' &&
{
--p;
isspace(p[-1]))
--p;
p[0] = '\0';
break;
}
}
if (buf[0] == '\0')
continue;
message("%s to %s",
"forward %.200s => %s",
if (forwarding &&
MaxForwardEntries > 0 &&
{
/* just stop reading and processing further entries */
#if 0
/* additional: (?) */
#endif /* 0 */
syserr("Attempt to forward to more than %d addresses (in %s)!",
maxreached = true;
}
}
{
if (aliaslevel <= MaxAliasRecursion ||
{
{
sm_dprintf("include: QS_DONTSEND ");
}
}
}
return rval;
}
static void
int ignore;
{
/*
** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
** DOING.
*/
}
/*
** SENDTOARGV -- send to an argument vector.
**
** Parameters:
** argv -- argument vector to send to.
** e -- the current envelope.
**
** Returns:
** none.
**
** Side Effects:
** puts all addresses on the argument vector onto the
** send queue.
*/
void
register char **argv;
register ENVELOPE *e;
{
register char *p;
}
/*
** GETCTLADDR -- get controlling address from an address header.
**
** If none, get one corresponding to the effective userid.
**
** Parameters:
** a -- the address to find the controller of.
**
** Returns:
** the controlling address.
*/
ADDRESS *
getctladdr(a)
register ADDRESS *a;
{
a = a->q_alias;
return a;
}
/*
** SELF_REFERENCE -- check to see if an address references itself
**
** The check is done through a chain of aliases. If it is part of
** a loop, break the loop at the "best" address, that is, the one
** that exists as a real user.
**
** This is to handle the case of:
** awc: Andrew.Chang
** Andrew.Chang: awc@mail.server
** which is a problem only on mail.server.
**
** Parameters:
** a -- the address to check.
**
** Returns:
** The address that should be retained.
*/
static ADDRESS *
ADDRESS *a;
{
ADDRESS *b; /* top entry in self ref loop */
ADDRESS *c; /* entry that point to a real mail box */
{
if (sameaddr(a, b))
break;
}
if (b == NULL)
{
sm_dprintf("\t... no self ref\n");
return NULL;
}
/*
** Pick the first address that resolved to a real mail box
** i.e has a mbdb entry. The returned value will be marked
** QSELFREF in recipient(), which in turn will disable alias()
** from marking it as QS_IS_DEAD(), which mean it will be used
** as a deliverable address.
**
** The 2 key thing to note here are:
** 1) we are in a recursive call sequence:
** alias->sendtolist->recipient->alias
** 2) normally, when we return back to alias(), the address
** will be marked QS_EXPANDED, since alias() assumes the
** expanded form will be used instead of the current address.
** This behaviour is turned off if the address is marked
** QSELFREF. We set QSELFREF when we return to recipient().
*/
c = a;
while (c != NULL)
{
{
{
sm_dprintf("found\n");
/* ought to cache results here */
if (sameaddr(b, c))
return b;
else
return c;
}
sm_dprintf("failed\n");
}
else
{
/* if local delivery, compare usernames */
{
sm_dprintf("\t... local match (%s)\n",
c->q_user);
if (sameaddr(b, c))
return b;
else
return c;
}
}
sm_dprintf("\n");
c = c->q_alias;
}
return NULL;
}