static char SccsId
[] = "@(#)parseaddr.c 3.31 %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 */
if (c
== '$' && delim
== '\t')
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 DOLLAR
: /* $- etc. */
if (isascii(c
) && isdigit(c
))
case '$': /* literal $ */
case '+': /* match anything */
case '-': /* match one token */
case '=': /* match one token of class */
case '#': /* canonical net name */
case '@': /* canonical host name */
case ':': /* canonical user name */
syserr("prescan: unknown state %d", state
);
if (state
== EOTOK
|| state
== SPACE
)
if (q
>= &buf
[sizeof buf
- 5])
usrerr("Address too long");
(void) expand(mbuf
, q
, &buf
[sizeof buf
- 5]);
/* decide whether this represents end of token */
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
;
(void) expand("$o", buf
, &buf
[sizeof buf
- 1]);
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
& (1 << 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
);
/* figure out what net/mailer to use */
syserr("buildaddr: no net");
if (strcmp(*tv
, "error") == 0)
syserr("buildaddr: error: no user");
for (mp
= Mailer
; (m
= *mp
++) != NULL
; )
if (strcmp(m
->m_name
, *tv
) == 0)
syserr("buildaddr: unknown net %s", *tv
);
/* figure out what host (if any) */
if (!bitset(M_LOCAL
, m
->m_flags
))
syserr("buildaddr: no host");
/* 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.
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
);
printf("\tnext=%x, flags=%o, rmailer %d\n", a
->q_next
,
a
->q_flags
, a
->q_rmailer
);