SCCSID(@
(#)parseaddr.c 3.45 %G%);
** PARSE -- 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.
** copyf -- determines what shall be copied:
** -1 -- don't copy anything. The printname
** (q_paddr) is just addr, and the
** user & host are allocated internally
** 0 -- copy out the parsed user & host, but
** don't copy the printname.
** +1 -- copy everything.
** A pointer to the address descriptor header (`a' if
# define DELIMCHARS "$()<>,;\\\"\r\n" /* word delimiters */
register struct mailer
*m
;
extern ADDRESS
*buildaddr();
** Initialize and prescan address.
printf("\n--parse(%s)\n", addr
);
pvp
= prescan(addr
, '\0');
** Apply rewriting rules.
** See if we resolved to a real mailer.
if (pvp
[0][0] != CANONNET
)
usrerr("cannot resolve name");
** Build canonical address from pvp.
** Make local copies of the host & user and then
a
->q_paddr
= newstr(addr
);
a
->q_host
= newstr(a
->q_host
);
if (a
->q_user
!= a
->q_paddr
)
a
->q_user
= newstr(a
->q_user
);
** Do UPPER->lower case mapping unless inhibited.
if (!bitset(M_HST_UPPER
, m
->m_flags
))
if (!bitset(M_USR_UPPER
, m
->m_flags
))
** PRESCAN -- Prescan name and make it canonical
** Scans a name and turns it into canonical form. This involves
** deleting blanks, comments (in parentheses), and turning the
** word "at" into an at-sign ("@"). The name is copied as this
** is done; it is legal to copy a name onto itself, since this
** process can only make things smaller.
** 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.
** are moving in place; set buflim to high core.
** A pointer to a vector of tokens.
static char buf
[MAXNAME
+MAXATOM
];
static char *av
[MAXATOM
+1];
for (p
= addr
; *p
!= '\0' && *p
!= delim
; )
while ((c
= *p
++) != '\0' && c
!= delim
)
/* chew up special characters */
case QSTRING
: /* in quoted string */
case ATOM
: /* regular atom */
case GETONE
: /* grab one character */
case EOTOK
: /* after atom or q-string */
case SPACE
: /* linear white space */
case OPER
: /* operator */
case ONEMORE
: /* $- etc. */
syserr("prescan: unknown state %d", state
);
if (state
== EOTOK
|| state
== SPACE
)
if (q
>= &buf
[sizeof buf
- 5])
usrerr("Address too long");
/* decide whether this represents end of token */
if (state
== OPER
|| state
== GETONE
)
if (c
== '\0' || c
== delim
)
usrerr("Unbalanced ')'");
usrerr("multiple < spec");
/* we prefer using machine readable name */
usrerr("Unbalanced `>'");
syserr("prescan: too many tokens");
usrerr("Unbalanced '('");
usrerr("Unbalanced '<'");
else if (state
== QSTRING
)
usrerr("Unbalanced '\"'");
** TOKTYPE -- return token type
** c -- the character in question.
static bool firstime
= TRUE
;
expand("$o", buf
, &buf
[sizeof buf
- 1], CurEnv
);
(void) strcat(buf
, DELIMCHARS
);
if (c
== MATCHCLASS
|| c
== MATCHREPL
)
if (iscntrl(c
) || index(buf
, c
) != NULL
)
** 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 (MATCHANY, MATCHONE,
** MATCHCLASS) 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 a MATCHONE or a MATCHCLASS
** we must also back out the match in mvp. If we reach a
** MATCHANY we just extend the match and start over again.
** When we finally match, we rewrite the address vector
** pvp -- pointer to token vector.
char **first
; /* first token matched */
char **last
; /* last token matched */
# define MAXMATCH 9 /* max params per rewrite */
register char *ap
; /* address pointer */
register char *rp
; /* rewrite pointer */
register char **avp
; /* address vector pointer */
register char **rvp
; /* rewrite vector pointer */
struct rewrite
*rwr
; /* pointer to current rewrite rule */
struct match mlist
[MAXMATCH
]; /* stores match on LHS */
struct match
*mlp
; /* cur ptr into mlist */
char *npvp
[MAXATOM
+1]; /* temporary space for rebuild */
printf("rewrite: original pvp:\n");
** Run through the list of rewrite rules, applying
for (rwr
= RewriteRules
[ruleset
]; rwr
!= NULL
; )
printf("-----trying rule:\n");
/* try to match on this rule */
for (rvp
= rwr
->r_lhs
, avp
= pvp
; *avp
!= NULL
; )
/* end-of-pattern before end-of-address */
/* match any token in a class */
s
= stab(ap
, ST_CLASS
, ST_FIND
);
if (s
== NULL
|| (s
->s_class
& (1L << class)) == 0)
/* explicit fall-through */
/* match exactly one token */
mlp
->first
= mlp
->last
= avp
++;
/* must have exact match */
/* successful match on this token */
/* match failed -- back up */
while (--rvp
>= rwr
->r_lhs
)
/* extend binding and continue */
if (*rp
== MATCHONE
|| *rp
== MATCHCLASS
)
/* total failure to match */
** See if we successfully matched
if (rvp
>= rwr
->r_lhs
&& *rvp
== NULL
)
printf("-----rule matches:\n");
for (rvp
= rwr
->r_rhs
, avp
= npvp
; *rvp
!= NULL
; rvp
++)
register struct match
*m
;
} while (pp
++ != m
->last
);
if (avp
>= &npvp
[MAXATOM
])
syserr("rewrite: expansion too long");
} while (pp
++ != m
->last
);
if (avp
>= &npvp
[MAXATOM
])
syserr("rewrite: expansion too long");
bmove((char *) npvp
, (char *) pvp
, (avp
- npvp
) * sizeof *avp
);
printf("rewritten as `");
for (vp
= pvp
; *vp
!= NULL
; vp
++)
if (pvp
[0][0] == CANONNET
)
printf("----- rule fails\n");
** BUILDADDR -- build address from token vector.
** a -- pointer to address descriptor to fill.
** If NULL, one will be allocated.
** NULL if there was an error.
static char buf
[MAXNAME
];
register struct mailer
*m
;
a
= (ADDRESS
*) xalloc(sizeof *a
);
clear((char *) a
, sizeof *a
);
/* figure out what net/mailer to use */
syserr("buildaddr: no net");
if (sameword(*tv
, "error"))
syserr("buildaddr: error: no user");
for (mp
= Mailer
; (m
= *mp
++) != NULL
; )
if (sameword(m
->m_name
, *tv
))
syserr("buildaddr: unknown net %s", *tv
);
/* figure out what host (if any) */
if (!bitset(M_LOCAL
, m
->m_flags
))
syserr("buildaddr: no host");
while (*tv
!= NULL
&& **tv
!= CANONUSER
)
(void) strcat(buf
, *tv
++);
/* figure out the user */
syserr("buildaddr: no user");
cataddr(++tv
, buf
, sizeof buf
);
** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
** pvp -- parameter vector to rebuild.
** buf -- buffer to build the string into.
while (*pvp
!= NULL
&& (i
= strlen(*pvp
)) < sz
)
natomtok
= (toktype(**pvp
) == ATOM
);
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.
** wildflg -- if TRUE, 'a' may have no user specified,
** in which case it is to match anything.
** TRUE -- they represent the same mailbox.
/* 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 ((!wildflg
|| a
->q_user
[0] != '\0') && strcmp(a
->q_user
, b
->q_user
) != 0)
/* if the mailer ignores hosts, we have succeeded! */
if (bitset(M_LOCAL
, a
->q_mailer
->m_flags
))
/* otherwise compare hosts (but be careful for NULL ptrs) */
if (a
->q_host
== NULL
|| b
->q_host
== NULL
)
if (strcmp(a
->q_host
, b
->q_host
) != 0)
** PRINTADDR -- print address (for debugging)
** a -- the address to print
** follow -- follow the q_next chain.
for (i
= indent
; i
> 0; i
--)
printf("%s: mailer %d (%s), host `%s', user `%s'\n", a
->q_paddr
,
a
->q_mailer
->m_mno
, a
->q_mailer
->m_name
, a
->q_host
, a
->q_user
);
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.
** force -- if set, forces rewriting even if the mailer
** does not request it. Used for rewriting
** 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
, force
)
static char buf
[MAXNAME
];
char *oldf
= macvalue('f');
char *oldx
= macvalue('x');
char *oldg
= macvalue('g');
extern ADDRESS
*buildaddr();
** See if this mailer wants the name to be rewritten. There are
** many problems here, owing to the standards for doing replies.
** In general, these names should only be rewritten if we are
** sending to another host that runs sendmail.
if (!bitset(M_RELRCPT
, m
->m_flags
) && !force
)
** Do general rewriting of name.
** This will also take care of doing global name translation.
define('x', getxpart(name
));
pvp
= prescan(name
, '\0');
/* oops... resolved to something */
cataddr(pvp
, lbuf
, sizeof lbuf
);
/* make the name relative to the receiving mailer */
expand(m
->m_from
, buf
, &buf
[sizeof buf
- 1], CurEnv
);
/* rewrite to get rid of garbage we added in the expand above */
pvp
= prescan(buf
, '\0');
cataddr(pvp
, lbuf
, sizeof lbuf
);
/* now add any comment info we had before back */
expand("$q", buf
, &buf
[sizeof buf
- 1], CurEnv
);
printf("remotename(%s) => `%s'\n", name
, buf
);
** CANONNAME -- make name canonical
** This is used for SMTP and misc. printing. Given a print
** address, it strips out comments, etc., and puts on exactly
** name -- the name to make canonical.
** pointer to canonical name.
** result is saved in static buf; future calls will trash it.
static char nbuf
[MAXNAME
+ 2];