* 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
[] = "@(#)parseaddr.c 8.37 (Berkeley) %G%";
static int toktype
__P((char));
#else /* !CC_WONT_PROMOTE */
static int toktype
__P((int)); /* char -> int */
#endif /* CC_WONT_PROMOTE */
static void _rewrite
__P((char **, int));
static void callsubr
__P((char **));
static ADDRESS
* buildaddr
__P((char **, ADDRESS
*));
static void uurelativize
__P((const char *, const char *, char **));
char *DelimChar
; /* set to point to the delimiter */
** PARSEADDR -- Parse an address
** Parses an address and breaks it up into three parts: a
** net to transmit the message on, the host to transmit it
** to, and a user on that host. These are loaded into an
** ADDRESS header with the values squirreled away if necessary.
** The "user" part may not be a real user; the process may
** just reoccur on that machine. For example, on a machine
** with an arpanet connection, the address
** will break up to a "user" of 'csvax.bill' and a host
** of 'berkeley' -- to be transmitted over the arpanet.
** addr -- the address to parse.
** a -- a pointer to the address descriptor buffer.
** If NULL, a header will be created.
** flags -- describe detail for parsing. See RF_ definitions
** delim -- the character to terminate the address, passed
** delimptr -- if non-NULL, set to the location of the
** delim character that was found.
** e -- the envelope that will contain this address.
** A pointer to the address descriptor header (`a' if
/* following delimiters are inherent to the internal algorithms */
# define DELIMCHARS "()<>,;\r\n" /* default word delimiters */
parseaddr(addr
, a
, flags
, delim
, delimptr
, e
)
** Initialize and prescan address.
printf("\n--parseaddr(%s)\n", addr
);
extern char *DelimChar
; /* parseaddr.c */
extern char *finddelim();
extern bool invalidaddr();
DelimChar
= finddelim(addr
, delim
);
invalid
= invalidaddr(addr
);
pvp
= prescan(addr
, delim
, pvpbuf
, sizeof pvpbuf
, delimptr
);
printf("parseaddr-->NULL\n");
if (invalidaddr(addr
, delim
== '\0' ? NULL
: *delimptr
))
printf("parseaddr-->bad address\n");
** Save addr if we are going to have to.
** We have to do this early because there is a chance that
** the map lookups in the rewriting rules could clobber
** static memory somewhere.
if (bitset(RF_COPYPADDR
, flags
) && addr
!= NULL
)
e
->e_to
= addr
= newstr(addr
);
** Apply rewriting rules.
** Ruleset 0 does basic parsing. It must resolve.
if (rewrite(pvp
, 3, 0, e
) == EX_TEMPFAIL
)
if (rewrite(pvp
, 0, 0, e
) == EX_TEMPFAIL
)
** Build canonical address from pvp.
a
= buildaddr(pvp
, a
, flags
, e
);
** Make local copies of the host & user and then
allocaddr(a
, flags
, addr
);
if (bitset(QBADADDR
, a
->q_flags
))
** If there was a parsing failure, mark it for queueing.
char *msg
= "Transient parse error -- message queued for future delivery";
printf("parseaddr: queuing message\n");
if (e
->e_message
== NULL
)
e
->e_message
= newstr(msg
);
** INVALIDADDR -- check for address containing meta-characters
** addr -- the address to check.
** TRUE -- if the address has any "wierd" characters
invalidaddr(addr
, delimptr
)
if (strcmp(addr
, "INvalidADDR") == 0)
usrerr("553 INvalid ADDRess");
for (; *addr
!= '\0'; addr
++)
if ((*addr
& 0340) == 0200)
if (savedelim
!= '\0' && delimptr
!= NULL
)
usrerr("553 Address contained invalid control characters");
if (savedelim
!= '\0' && delimptr
!= NULL
)
** ALLOCADDR -- do local allocations of address on demand.
** Also lowercases the host name if requested.
** a -- the address to reallocate.
** flags -- the copy flag (see RF_ definitions in sendmail.h
** paddr -- the printname of the address.
** Copies portions of a into local buffers as requested.
allocaddr(a
, flags
, paddr
)
printf("allocaddr(flags=%x, paddr=%s)\n", flags
, paddr
);
if (bitset(RF_COPYPARSE
, flags
))
a
->q_host
= newstr(a
->q_host
);
if (a
->q_user
!= a
->q_paddr
)
a
->q_user
= newstr(a
->q_user
);
** INVALIDADDR -- check an address string for invalid control characters.
** addr -- address string to be checked.
** TRUE if address string could cause problems, FALSE o/w.
** ExitStat may be changed and an error message generated.
/* make sure error messages don't have garbage on them */
** Sendmail reserves characters 020 - 036 for rewriting rules
** which can cause havoc (e.g. infinite rewriting loops) if
** one shows up at the wrong time. If any of these characters
** appear in an address, the address is deemed "invalid" and
** an error message is generated.
for (cp
= addr
; *cp
; cp
++)
if ((*cp
>= MATCHZANY
&& *cp
<= HOSTEND
) || *cp
== '\001')
usrerr("address contained invalid control char(s)");
** PRESCAN -- Prescan name and make it canonical
** Scans a name and turns it into a set of tokens. This process
** deletes blanks and comments (in parentheses).
** This routine knows about quoted strings and angle brackets.
** There are certain subtleties to this routine. The one that
** comes to mind now is that backslashes on the ends of names
** are silently stripped off; this is intentional. The problem
** is that some versions of sndmsg (like at LBL) set the kill
** character to something other than @ when reading addresses;
** so people type "csvax.eric\@berkeley" -- which screws up the
** addr -- the name to chomp.
** delim -- the delimiter for the address, normally
** '\0' or ','; \0 is accepted in any case.
** If '\t' then we are reading the .cf file.
** pvpbuf -- place to put the saved text -- note that
** the pointers are static.
** pvpbsize -- size of pvpbuf.
** delimptr -- if non-NULL, set to the location of the
** terminating delimiter.
** A pointer to a vector of tokens.
/* states and character types */
# define OPR 0 /* operator */
# define ATM 1 /* atom */
# define QST 2 /* in quoted string */
# define SPC 3 /* chewing up spaces */
# define ONE 4 /* pick up one character */
# define NSTATES 5 /* number of states */
# define TYPE 017 /* mask to select state type */
/* meta bits for table */
# define M 020 /* meta character; don't pass through */
# define B 040 /* cause a break */
# define MB M|B /* meta-break */
static short StateTab
[NSTATES
][NSTATES
] =
/* oldst chtype> OPR ATM QST SPC ONE */
/*OPR*/ OPR
|B
, ATM
|B
, QST
|B
, SPC
|MB
, ONE
|B
,
/*ATM*/ OPR
|B
, ATM
, QST
|B
, SPC
|MB
, ONE
|B
,
/*QST*/ QST
, QST
, OPR
, QST
, QST
,
/*SPC*/ OPR
, ATM
, QST
, SPC
|M
, ONE
,
/*ONE*/ OPR
, OPR
, OPR
, OPR
, OPR
,
/* token type table -- it gets modified with $o characters */
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,SPC
,SPC
,SPC
,SPC
,SPC
,ATM
,ATM
,
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
SPC
,ATM
,QST
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,SPC
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
OPR
,OPR
,ONE
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,
OPR
,OPR
,OPR
,ONE
,ONE
,ONE
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,OPR
,
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,ATM
,
#define toktype(c) ((int) TokTypeTab[(c) & 0xff])
# define NOCHAR -1 /* signal nothing in lookahead token */
prescan(addr
, delim
, pvpbuf
, pvpbsize
, delimptr
)
char *saveto
= CurEnv
->e_to
;
static char *av
[MAXATOM
+1];
static char firsttime
= TRUE
;
/* initialize the token type table */
expand("\201o", obuf
, &obuf
[sizeof obuf
- sizeof DELIMCHARS
], CurEnv
);
strcat(obuf
, DELIMCHARS
);
for (p
= obuf
; *p
!= '\0'; p
++)
if (TokTypeTab
[*p
& 0xff] == ATM
)
TokTypeTab
[*p
& 0xff] = OPR
;
/* make sure error messages don't have garbage on them */
/* store away any old lookahead character */
if (c
!= NOCHAR
&& !bslashmode
)
/* see if there is room */
if (q
>= &pvpbuf
[pvpbsize
- 5])
usrerr("553 Address too long");
/* read a new input character */
/* diagnose and patch up bad syntax */
usrerr("653 Unbalanced '\"'");
usrerr("653 Unbalanced '('");
usrerr("653 Unbalanced '<'");
else if (c
== delim
&& anglecnt
<= 0 &&
cmntcnt
<= 0 && state
!= QST
)
printf("c=%c, s=%d; ", c
, state
);
/* chew up special characters */
/* kludge \! for naive users */
else if (c
!= '!' || state
== QST
)
/* do nothing, just avoid next clauses */
usrerr("653 Unbalanced ')'");
usrerr("653 Unbalanced '>'");
else if (delim
== ' ' && isascii(c
) && isspace(c
))
else if (c
== ':' && !CurEnv
->e_oldstyle
)
/* consume characters until a semicolon */
while (*p
!= '\0' && *p
!= ';')
usrerr("Unbalanced ':...;' group spec");
else if (c
== ';') /* semicolons are not tokens */
/* see if this is end of input */
if (c
== delim
&& anglecnt
<= 0 && state
!= QST
)
newstate
= StateTab
[state
][toktype(c
)];
printf("ns=%02o\n", newstate
);
syserr("553 prescan: too many tokens");
syserr("553 prescan: token too long");
} while (c
!= '\0' && (c
!= delim
|| anglecnt
> 0));
printf("prescan: null leading token\n");
** REWRITE -- apply rewrite rules to token vector.
** This routine is an ordered production system. Each rewrite
** rule has a LHS (called the pattern) and a RHS (called the
** rewrite); 'rwr' points the the current rewrite rule.
** For each rewrite rule, 'avp' points the address vector we
** are trying to match against, and 'pvp' points to the pattern.
** If pvp points to a special match value (MATCHZANY, MATCHANY,
** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
** matched is saved away in the match vector (pointed to by 'mvp').
** When a match between avp & pvp does not match, we try to
** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
** we must also back out the match in mvp. If we reach a
** MATCHANY or MATCHZANY we just extend the match and start
** When we finally match, we rewrite the address vector
** pvp -- pointer to token vector.
** ruleset -- the ruleset to use for rewriting.
** reclevel -- recursion level (to catch loops).
** e -- the current envelope.
** A status code. If EX_TEMPFAIL, higher level code should
# define OP_NONZLEN 00001
char **first
; /* first token matched */
char **last
; /* last token matched */
char **pattern
; /* pointer to pattern */
char **source
; /* left hand source operand */
char flags
; /* attributes of this operator */
# define MAXMATCH 9 /* max params per rewrite */
# ifndef MAXRULERECURSION
# define MAXRULERECURSION 50 /* max recursion depth */
static char control_opts
[MAX_CONTROL
];
static char control_init_data
[] = {
MATCHANY
, OP_VARLEN
|OP_NONZLEN
,
MATCHCLASS
, OP_NONZLEN
|OP_VARLEN
|OP_CLASS
,
rewrite(pvp
, ruleset
, reclevel
, e
)
register char *ap
; /* address pointer */
register char *rp
; /* rewrite pointer */
register char **avp
; /* address vector pointer */
register char **rvp
; /* rewrite vector pointer */
register struct match
*mlp
; /* cur ptr into mlist */
register struct rewrite
*rwr
; /* pointer to current rewrite rule */
int ruleno
; /* current rule number */
int rstat
= EX_OK
; /* return status */
int subr
; /* subroutine number if >= 0 */
bool dolookup
; /* do host aliasing */
char *npvp
[MAXATOM
+1]; /* temporary space for rebuild */
char tokbuf
[MAXNAME
+1]; /* for concatenated class tokens */
int nloops
, nmatches
= 0; /* for looping rule checks */
struct rewrite
*prev_rwr
; /* pointer to previous rewrite rule */
struct match mlist
[MAXMATCH
+1]; /* stores match on LHS */
struct match
*old_mlp
; /* to save our place */
bool extend_match
; /* extend existing match during backup */
if (OpMode
== MD_TEST
|| tTd(21, 1))
printf("rewrite: ruleset %2d input:", ruleset
);
if (ruleset
< 0 || ruleset
>= MAXRWSETS
)
syserr("554 rewrite: illegal ruleset number %d", ruleset
);
if (reclevel
++ > MAXRULERECURSION
)
syserr("rewrite: infinite recursion, ruleset %d", ruleset
);
buf
[0] = buf
[MAXLINE
-1] = 0;
(void) strncat(buf
, *pvp
++, sizeof buf
);
syserr("address causes rewrite loop: <%s>", buf
);
/* Be sure to recognize first rule as new */
** Run through the list of rewrite rules, applying any that match.
for (rwr
= RewriteRules
[ruleset
]; rwr
!= NULL
; )
printf("-----trying rule:");
** Set up the match list. This is done once for each
** rule. If a rule is used repeatedly, the list need not
** be set up the next time.
for (rvp
= rwr
->r_lhs
, mlp
= mlist
;
*rvp
&& (mlp
< &mlist
[MAXMATCH
]); rvp
++)
mlp
->flags
= ((unsigned char) **rvp
>= MAX_CONTROL
) ?
0 : control_opts
[**rvp
] ;
syserr("Too many variables on LHS in ruleset %d", ruleset
);
/* Make sure end marker is initialized */
/* try to match on this rule */
syserr("Looping on ruleset %d, rule %d",
ruleset
, rwr
-RewriteRules
[ruleset
]);
mlp
= mlist
- 1; /* force rule failure */
syserr("554 Infinite loop in ruleset %d, rule %d",
while ((ap
= *avp
) != NULL
|| *rvp
!= NULL
)
mlp
->last
= mlp
->flags
== 0 || (mlp
->flags
& OP_NONZLEN
) ?
/* end-of-pattern before end-of-address */
/* Premature end of address */
if (ap
== NULL
&& avp
!= mlp
->last
)
** Simplest case - exact token comparison between
** pattern and address. Such a match is not saved
if (ap
== NULL
|| strcasecmp(ap
, rp
))
** This is special case handled. The match is exact,
** but might span zero or more address tokens. The
** result is saved in mlp.
rp
= macvalue(rp
[1], CurEnv
);
if (*avp
== NULL
|| strncasecmp(rp
,*avp
,len
= strlen(*avp
)))
** All other matches are saved in mlp. Initially
** assume they match at the shortest possible length
** for this pattern. Variable patterns will be
** extended later as needed.
if (!(mlp
->flags
& OP_VARLEN
))
** Match against run-time macro.
** This algorithm is broken for the
** general case (no recursive macros,
** improper tokenization) but should
** work for the usual cases.
printf("rewrite: LHS $&%c => \"%s\"\n",
ap
== NULL
? "(NULL)" : ap
);
strncasecmp(ap
, *avp
, strlen(*avp
)) != 0)
** We now have a variable length item. It could
** be $+ or $* in which case no special checking
** is needed. But a class match such as $=x must
** As a speedup, if a variable length item is
** followed by a plain character token, we initially
** extend the match to the first such token we find.
** If the required character token cannot be found,
** we fail the match at this point.
/* If next token is char token */
if (&rvp
[1] < mlp
[1].source
)
while (*avp
&& strcasecmp(*avp
, rvp
[1]))
** If we can't find the proper ending token,
** leave avp point to NULL. This indicates
** we have run out of address tokens. It is
** pointless to advance the beginning of this
/* next token is end of address */
if (mlp
->flags
& OP_CLASS
)
register char *cp
= tokbuf
;
s
= stab(tokbuf
, ST_CLASS
, ST_FIND
);
if (s
!= NULL
&& bitnset(rp
[1], s
->s_class
))
** Class match initially failed.
** Extend the tentative match.
** Again, if followed by a character
** token, extend all the way to that
** token before checking.
if (&rvp
[1] < mlp
[1].source
)
while (*(mlp
->last
) && strcasecmp(*(mlp
->last
), rvp
[1]))
if (*(mlp
->last
) == NULL
)
** ending token. But we had
** found ending tokens before.
** A match is still plausible
** tentative match is advanced.
** Hence we must not leave avp
/* We failed to match. mlp marks point of failure */
** There is a special case when we have exhausted
** the address, but have not exhausted the pattern.
** Under normal circumstances we could consider the
** failure permanent, since extending the number of
** address tokens matched by a '$+' or a '$*' will
** only worsen the situation.
** There is an exception, however. It is possible
** that we have matched a class token, say '$=x',
** with three or more tokens. Extending a '$+' say,
** which precedes the '$=x' will move the beginning
** of the '$=x' match to the right, but it might match
** a smaller number of tokens then, possibly
** correcting the mismatch.
** Thus in this case we initially back up to the
** $=x which matches three or more tokens.
if ((mlp
->flags
& OP_CLASS
) &&
mlp
->last
> 2 + mlp
->first
)
** Now backup till we find a match with a pattern
** whose length is extendable, and extend that.
while (mlp
>= mlist
&& !(mlp
->flags
& OP_VARLEN
))
/* Total failure to match */
** We have found a backup point. Normally we would
** increase the matched amount by one token, and
** continue from the next item in the pattern. But
** there are two special cases. If this is a
** class-type match (OP_CLASS), we must test the
** validity of the extended match. If this pattern
** item is directly followed by a character token, it
** is worth going back and locating the next such
** character token before we continue on.
if ((mlp
->flags
& OP_CLASS
) || (&rvp
[1] < mlp
[1].source
))
** See if we successfully matched.
printf("----- rule fails\n");
syserr("Loop in ruleset %d, rule %d (too many matches)",
ruleset
, rwr
- RewriteRules
[ruleset
]);
printf("-----rule matches:");
if ((*rp
& 0377) == CANONUSER
)
else if ((*rp
& 0377) == CANONHOST
)
else if ((*rp
& 0377) == CANONNET
)
for (avp
= npvp
; *rvp
!= NULL
; rvp
++)
register struct match
*m
;
/* check to see if we should do a lookup */
/* see if there is substitution here */
if (*rp
== MATCHREPL
&& rp
[1] >= '1' && rp
[1] <= '9')
/* substitute from LHS */
if (m
< mlist
|| m
>= mlp
)
syserr("rewrite: ruleset %d: replacement #%c out of bounds",
if (avp
>= &npvp
[MAXATOM
])
/* vanilla replacement */
if (avp
>= &npvp
[MAXATOM
])
char *p
= macvalue(rp
[1], CurEnv
);
printf("expanding runtime macro '%c' to \"%s\"\n",
rp
[1], p
? p
: "(null)");
** Check for any hostname/keyword lookups.
for (rvp
= npvp
; *rvp
!= NULL
; rvp
++)
char hbuf
[MAXNAME
+ 1], ubuf
[MAXNAME
+ 1];
char *pvpb1
[MAXATOM
+ 1];
if ((**rvp
& 0377) != HOSTBEGIN
&&
(**rvp
& 0377) != LOOKUPBEGIN
)
** Got a hostname/keyword lookup.
** This could be optimized fairly easily.
if ((**rvp
& 0377) == HOSTBEGIN
)
map
= stab(mapname
, ST_MAP
, ST_FIND
);
syserr("554 rewrite: map %s not found", mapname
);
/* extract the match part */
while (*rvp
!= NULL
&& (**rvp
& 0377) != endtoken
)
int nodetype
= **rvp
& 0377;
if (nodetype
!= CANONHOST
&& nodetype
!= CANONUSER
)
cataddr(xpvp
, NULL
, replac
,
&pvpbuf
[sizeof pvpbuf
] - replac
,
replac
+= strlen(replac
) + 1;
cataddr(xpvp
, NULL
, replac
,
&pvpbuf
[sizeof pvpbuf
] - replac
,
/* save the remainder of the input string */
trsize
= (int) (avp
- rvp
+ 1) * sizeof *rvp
;
bcopy((char *) rvp
, (char *) pvpb1
, trsize
);
/* append it to the token list */
for (avp
= hbrvp
; *xpvp
!= NULL
; xpvp
++)
if (avp
>= &npvp
[MAXATOM
])
/* restore the old trailing information */
for (xpvp
= pvpb1
; *xpvp
!= NULL
; xpvp
++)
if (defaultpart
&& **xpvp
== HOSTEND
)
else if (!defaultpart
|| !match
)
if (avp
>= &npvp
[MAXATOM
])
** Check for subroutine calls.
** Then copy vector back into original space.
for (avp
= npvp
; *avp
++ != NULL
;);
** Copy result back to original string.
for (avp
= pvp
; *rvp
!= NULL
; rvp
++)
** If this specified a subroutine, call it.
printf("-----callsubr %s\n", subr
);
** Done with rewriting this pass.
if (OpMode
== MD_TEST
|| tTd(21, 1))
printf("rewrite: ruleset %2d returns:", ruleset
);
** CALLSUBR -- call subroutines in rewrite vector
** pvp -- pointer to token vector.
for (; *pvp
!= NULL
; pvp
++)
if (**pvp
== CALLSUBR
&& pvp
[1] != NULL
&& isdigit(pvp
[1][0]))
printf("-----callsubr %d\n", subr
);
** Take care of possible inner calls.
** Move vector up over calling opcode.
for (rvp
= pvp
+2; *rvp
!= NULL
; rvp
++)
** Call inferior ruleset.
** BUILDADDR -- build address from token vector.
** a -- pointer to address descriptor to fill.
** If NULL, one will be allocated.
** flags -- info regarding whether this is a sender or
** e -- the current envelope.
** NULL if there was an error.
char *ec_name
; /* name of error code */
int ec_code
; /* numeric code */
"unavailable", EX_UNAVAILABLE
,
buildaddr(tv
, a
, flags
, e
)
register struct mailer
*m
;
static MAILER errormailer
;
static char *errorargv
[] = { "ERROR", NULL
};
static char buf
[MAXNAME
];
printf("buildaddr, flags=%x, tv=", flags
);
a
= (ADDRESS
*) xalloc(sizeof *a
);
clear((char *) a
, sizeof *a
);
/* figure out what net/mailer to use */
if (*tv
== NULL
|| **tv
!= CANONNET
)
syserr("554 buildaddr: no net");
a
->q_mailer
= &errormailer
;
if (errormailer
.m_name
== NULL
)
/* initialize the bogus mailer */
errormailer
.m_name
= "*error*";
errormailer
.m_mailer
= "ERROR";
errormailer
.m_argv
= errorargv
;
if (strcasecmp(*tv
, "error") == 0)
if ((**++tv
& 0377) == CANONHOST
)
register struct errcodes
*ep
;
if (isascii(**++tv
) && isdigit(**tv
))
for (ep
= ErrorCodes
; ep
->ec_name
!= NULL
; ep
++)
if (strcasecmp(ep
->ec_name
, *tv
) == 0)
for (; (*tv
!= NULL
) && (**tv
!= CANONUSER
); tv
++)
if ((**tv
& 0377) != CANONUSER
)
syserr("554 buildaddr: error: no user");
cataddr(++tv
, NULL
, buf
, sizeof buf
, ' ');
syslog (LOG_DEBUG
, "%s: Trace: $#ERROR $: %s",
if (isascii(buf
[0]) && isdigit(buf
[0]) &&
isascii(buf
[1]) && isdigit(buf
[1]) &&
isascii(buf
[2]) && isdigit(buf
[2]) &&
** If this is a 4xx code and we aren't running
** SMTP on our input, bounce this message;
** otherwise it disappears without a trace.
if (fmt
[0] == '4' && OpMode
!= MD_SMTP
&&
e
->e_flags
|= EF_FATALERRS
;
for (mp
= Mailer
; (m
= *mp
++) != NULL
; )
if (strcasecmp(m
->m_name
, *tv
) == 0)
syserr("554 buildaddr: unknown mailer %s", *tv
);
/* figure out what host (if any) */
while (*++tv
!= NULL
&& **tv
!= CANONUSER
)
/* figure out the user */
if (*tv
== NULL
|| (**tv
& 0377) != CANONUSER
)
syserr("554 buildaddr: no user");
/* do special mapping for local mailer */
if (*p
== '|' && bitnset(M_CHECKPROG
, m
->m_flags
))
a
->q_mailer
= m
= ProgMailer
;
else if (*p
== '/' && bitnset(M_CHECKFILE
, m
->m_flags
))
a
->q_mailer
= m
= FileMailer
;
else if (*p
== ':' && bitnset(M_CHECKINCLUDE
, m
->m_flags
))
cataddr(tv
, NULL
, buf
, sizeof buf
, '\0');
if (strncasecmp(buf
, ":include:", 9) == 0)
/* if :include:, don't need further rewriting */
a
->q_mailer
= m
= InclMailer
;
if (bitnset(M_CHECKUDB
, m
->m_flags
) && *tv
!= NULL
&&
a
->q_flags
|= QNOTREMOTE
;
rewrite(tv
, m
->m_r_rwset
);
(void) rewrite(tv
, 4, 0, e
);
/* save the result for the command line/RCPT argument */
cataddr(tv
, NULL
, buf
, sizeof buf
, '\0');
** Do mapping to lower case as requested by mailer
if (a
->q_host
!= NULL
&& !bitnset(M_HST_UPPER
, m
->m_flags
))
if (!bitnset(M_USR_UPPER
, m
->m_flags
))
** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
** pvp -- parameter vector to rebuild.
** evp -- last parameter to include. Can be NULL to
** buf -- buffer to build the string into.
** spacesub -- the space separator character; if null,
cataddr(pvp
, evp
, buf
, sz
, spacesub
)
while (*pvp
!= NULL
&& (i
= strlen(*pvp
)) < sz
)
natomtok
= (toktype(**pvp
) == ATM
);
if (oatomtok
&& natomtok
)
** SAMEADDR -- Determine if two addresses are the same
** This is not just a straight comparison -- if the mailer doesn't
** care about the host we just ignore it, etc.
** a, b -- pointers to the internal forms to compare.
** TRUE -- they represent the same mailbox.
register ADDRESS
*ca
, *cb
;
/* if they don't have the same mailer, forget it */
if (a
->q_mailer
!= b
->q_mailer
)
/* if the user isn't the same, we can drop out */
if (strcasecmp(a
->q_user
, b
->q_user
))
/* if we have good uids for both but they differ, these are different */
if (a
->q_mailer
== ProgMailer
)
if (ca
!= NULL
&& cb
!= NULL
&&
bitset(QGOODUID
, ca
->q_flags
& cb
->q_flags
) &&
/* otherwise compare hosts (but be careful for NULL ptrs) */
if (a
->q_host
== b
->q_host
)
/* probably both null pointers */
if (a
->q_host
== NULL
|| b
->q_host
== NULL
)
/* only one is a null pointer */
if (strcasecmp(a
->q_host
, b
->q_host
))
** PRINTADDR -- print address (for debugging)
** a -- the address to print
** follow -- follow the q_next chain.
for (i
= indent
; i
> 0; i
--)
/* find the mailer -- carefully */
for (i
= indent
; i
> 0; i
--)
printf("\tnext=%x, flags=%o, rmailer %d, alias=%x, sibling=%x, child=%x\n",
a
->q_next
, a
->q_flags
, a
->q_rmailer
, a
->q_alias
,
a
->q_sibling
, a
->q_child
);
/* follow the chain if appropriate */
printaddr(a
->q_child
, TRUE
);
** REMOTENAME -- return the name relative to the current mailer
** name -- the name to translate.
** m -- the mailer that we want to do rewriting relative
** flags -- fine tune operations.
** pstat -- pointer to status word.
** e -- the current envelope.
** the text string representing this address relative to
** The text string returned is tucked away locally;
** copy it if you intend to save it.
remotename(name
, m
, flags
, pstat
, e
)
char *oldg
= macvalue('g', e
);
static char buf
[MAXNAME
];
printf("remotename(%s)\n", name
);
/* don't do anything if we are tagging it as special */
if ((senderaddress
? m
->m_s_rwset
: m
->m_r_rwset
) < 0)
** Do a heuristic crack of this name to extract any comment info.
** This will leave the name as a comment and a $g macro.
if (bitset(RF_CANONICAL
, flags
) || bitnset(M_NOCOMMENT
, m
->m_flags
))
** Turn the name into canonical form.
** Normally this will be RFC 822 style, i.e., "user@domain".
** If this only resolves to "user", and the "C" flag is
** specified in the sending mailer, then the sender's
** domain will be appended.
pvp
= prescan(name
, '\0', pvpbuf
, sizeof pvpbuf
, NULL
);
if (rewrite(pvp
, 3, 0, e
) == EX_TEMPFAIL
)
if (bitset(RF_ADDDOMAIN
, flags
) && e
->e_fromdomain
!= NULL
)
/* append from domain to this address */
register char **pxp
= pvp
;
/* see if there is an "@domain" in the current name */
while (*pxp
!= NULL
&& strcmp(*pxp
, "@") != 0)
/* no.... append the "@domain" from the sender */
register char **qxq
= e
->e_fromdomain
;
while ((*pxp
++ = *qxq
++) != NULL
)
if (rewrite(pvp
, 3, 0, e
) == EX_TEMPFAIL
)
** Do more specific rewriting.
** Rewrite using ruleset 1 or 2 for envelope addresses and
** 5 or 6 for header addresses depending on whether this
** is a sender address or not.
** Then run it through any receiving-mailer-specific rulesets.
if (rewrite(pvp
, rwset
, 0, e
) == EX_TEMPFAIL
)
** Do any final sanitation the address may require.
** This will normally be used to turn internal forms
** (e.g., user@host.LOCAL) into external form. This
** may be used as a default to the above rules.
if (rewrite(pvp
, 4, 0, e
) == EX_TEMPFAIL
)
** Now restore the comment information we had at the beginning.
cataddr(pvp
, NULL
, lbuf
, sizeof lbuf
, '\0');
/* need to make sure route-addrs have <angle brackets> */
if (bitset(RF_CANONICAL
, flags
) && lbuf
[0] == '@')
expand("<\201g>", buf
, &buf
[sizeof buf
- 1], e
);
expand(fancy
, buf
, &buf
[sizeof buf
- 1], e
);
printf("remotename => `%s'\n", buf
);
** UURELATIVIZE -- Make an address !-relative to recipient/sender nodes
** from -- the sending node (usually "$k" or "$w")
** to -- the receiving node (usually "$h")
** The pvp is rewritten to be relative the "to" node
** wrt the "from" node. In other words, if the pvp
** is headed by "to!" that part is stripped; otherwise
** "from!" is prepended. Exception: "to!user" addresses
** with no '!'s in the user part are sent as is.
** The pvp may overflow, but we don't catch it.
uurelativize(from
, to
, pvp
)
register char **pxp
= pvp
;
char expfrom
[MAXNAME
], expto
[MAXNAME
];
expand(from
, expfrom
, &expfrom
[sizeof expfrom
- 1], CurEnv
);
expand(to
, expto
, &expto
[sizeof expto
- 1], CurEnv
);
* supposing that we've got something, should
* we add "from!" or remove "to!"?
if (pvp
[1] == NULL
|| strcmp(pvp
[1], "!") != 0 ||
/*strcasecmp?*/ strcmp(pvp
[0], expto
) != 0)
/* either local name, no UUCP address, */
/* or not to "to!" ==> prepend address with "from!" */
if (pvp
[1] == NULL
|| strcmp(pvp
[1], "!") != 0 ||
/*strcasecmp?*/ strcmp(pvp
[0], expfrom
) != 0)
pvp
[0] = newstr(expfrom
);
/* address is to "to!" -- remove if not "to!user" */
*pxp
!= NULL
&& strcmp(*pxp
, "!") != 0; pxp
++)
for (pxp
= pvp
; *pxp
!= NULL
; pxp
++)
** MAPLOCALUSER -- run local username through ruleset 5 for final redirection
** a -- the address to map (but just the user name part).
** sendq -- the sendq in which to install any replacement
maplocaluser(a
, sendq
, e
)
register ADDRESS
*a1
= NULL
;
printf("maplocaluser: ");
pvp
= prescan(a
->q_user
, '\0', pvpbuf
, sizeof pvpbuf
, &delimptr
);
(void) rewrite(pvp
, 5, 0, e
);
if (pvp
[0] == NULL
|| (pvp
[0][0] & 0377) != CANONNET
)
/* if non-null, mailer destination specified -- has it changed? */
a1
= buildaddr(pvp
, NULL
, 0, e
);
if (a1
== NULL
|| sameaddr(a
, a1
))
/* mark old address as dead; insert new address */
printf("maplocaluser: QDONTSEND ");
allocaddr(a1
, RF_COPYALL
, NULL
);
(void) recipient(a1
, sendq
, e
);
** DEQUOTE_INIT -- initialize dequote map
** map -- the internal map structure.
while (isascii(*p
) && isspace(*p
))
while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
if (map
->map_app
!= NULL
)
map
->map_app
= newstr(map
->map_app
);
** DEQUOTE_MAP -- unquote an address
** map -- the internal map structure (ignored).
** name -- the name to dequote.
** av -- arguments (ignored).
** statp -- pointer to status out-parameter.
** NULL -- if there were no quotes, or if the resulting
** unquoted buffer would not be acceptable to prescan.
** else -- The dequoted buffer.
dequote_map(map
, name
, av
, statp
)
for (p
= q
= name
; (c
= *p
++) != '\0'; )
if (c
== ' ' && SpaceSub
!= '\0')
if (anglecnt
!= 0 || cmntcnt
!= 0 || bslashmode
||
quotemode
|| quotecnt
<= 0 || spacecnt
!= 0)