/* mf.c - mail filter subroutines */
static char ident
[] = "@(#)$Id: mf.c,v 1.10 1992/12/15 00:20:22 jromine Exp $";
static int isat(), parse_address(), phrase();
static int route_addr(), local_part(), domain(), route();
if (!s
) { _cleanup(); abort(); for(;;) pause();}
if ((p
= malloc ((unsigned) (strlen (s
) + 2))) != NULL
)
static char *add (s1
, s2
)
if ((p
= malloc ((unsigned) (strlen (s1
) + strlen (s2
) + 2))) != NULL
)
(void) sprintf (p
, "%s%s", s2
, s1
);
return (strncmp (string
, "From ", 5) == 0
|| strncmp (string
, ">From ", 6) == 0);
char c1
= islower (*a
) ? toupper (*a
) : *a
;
char c2
= islower (*b
) ? toupper (*b
) : *b
;
* seekadrx() is tricky. We want to cover both UUCP-style and ARPA-style
* addresses, so for each list of addresses we see if we can find some
* character to give us a hint.
#define CHKADR 0 /* undertermined address style */
#define UNIXDR 1 /* UNIX-style address */
#define ARPADR 2 /* ARPAnet-style address */
static char *punctuators
= ";<>.()[]";
static struct adrx adrxs1
;
struct adrx
*seekadrx (addrs
)
static int state
= CHKADR
;
register struct adrx
*adrxp
;
for (state
= UNIXDR
, cp
= addrs
; *cp
; cp
++)
if (index (punctuators
, *cp
)) {
adrxp
= uucpadrx (addrs
);
* uucpadrx() implements a partial UUCP-style address parser. It's based
* on the UUCP notion that addresses are separated by spaces or commas.
struct adrx
*uucpadrx (addrs
)
register struct adrx
*adrxp
= &adrxs1
;
vp
= tp
= getcpy (addrs
);
for (cp
= tp
; isspace (*cp
); cp
++)
if ((wp
= index (cp
, ',')) == NULL
)
if ((wp
= index (cp
, ' ')) != NULL
) {
if (*xp
!= 0 && isat (--xp
)) {
if ((zp
= index (yp
, ' ')) != NULL
)
adrxp
-> text
= getcpy (cp
);
adrxp
-> host
= adrxp
-> path
= NULL
;
if ((wp
= rindex (cp
, '@')) != NULL
) {
adrxp
-> host
= *wp
? wp
: NULL
;
for (wp
= cp
+ strlen (cp
) - 4; wp
>= cp
; wp
--)
adrxp
-> pers
= adrxp
-> grp
= adrxp
-> note
= adrxp
-> err
= NULL
;
static void compress (fp
, tp
)
for (c
= ' ', cp
= tp
; (*tp
= *fp
++) != 0;)
return (strncmp (p
, " AT ", 4)
&& strncmp (p
, " At ", 4)
&& strncmp (p
, " aT ", 4)
&& strncmp (p
, " at ", 4) ? FALSE
: TRUE
);
* getadrx() implements a partial 822-style address parser. The parser
* is neither complete nor correct. It does however recognize nearly all
* of the 822 address syntax. In addition it handles the majority of the
* 733 syntax as well. Most problems arise from trying to accomodate both.
* In terms of 822, the route-specification in
* "<" [route] local-part "@" domain ">"
* is parsed and returned unchanged. Multiple at-signs are compressed
* via source-routing. Recursive groups are not allowed as per the
* In terms of 733, " at " is recognized as equivalent to "@".
* In terms of both the parser will not complain about missing hosts.
* We should not allow addresses like
* Marshall T. Rose <MRose@UCI>
* "Marshall T. Rose" <MRose@UCI>
* Unfortunately, a lot of mailers stupidly let people get away with this.
* We should not allow addresses like
* Unfortunately, a lot of mailers stupidly let people's UAs get away with
* We should not allow addresses like
* Marshall Rose <@UCI:MRose@UCI-750a>
* Unfortunately, a lot of mailers stupidly do this.
static int last_lex
= LX_END
;
static char *pers
= NULL
;
static char *mbox
= NULL
;
static char *host
= NULL
;
static char *path
= NULL
;
static char *note
= NULL
;
static struct adrx adrxs2
;
struct adrx
*getadrx (addrs
)
register struct adrx
*adrxp
= &adrxs2
;
pers
= mbox
= host
= path
= grp
= note
= NULL
;
dp
= cp
= getcpy (addrs
? addrs
: "");
switch (parse_address ()) {
default: /* catch trailing comments */
(void) sprintf (adr
, "%.*s", cp
- ap
, ap
);
bp
= adr
+ strlen (adr
) - 1;
if (*bp
== ',' || *bp
== ';' || *bp
== '\n')
adrxp
-> err
= err
[0] ? err
: NULL
;
static int parse_address () {
switch (my_lex (buffer
)) {
(void) strcpy (err
, "extraneous semi-colon");
case LX_LBRK
: /* sigh (2) */
case LX_AT
: /* sigh (3) */
if (route_addr (buffer
) == NOTOK
)
return OK
; /* why be choosy? */
(void) sprintf (err
, "illegal address construct (%s)", buffer
);
switch (my_lex (buffer
)) {
pers
= add (buffer
, add (" ", pers
));
more_phrase
: ; /* sigh (1) */
if (phrase (buffer
) == NOTOK
)
if (route_addr (buffer
) == NOTOK
)
(void) sprintf (err
, "missing right-bracket (%s)", buffer
);
(void) sprintf (err
, "nested groups not allowed (%s)",
switch (my_lex (buffer
)) {
case LX_END
: /* tsk, tsk */
case LX_DOT
: /* sigh (1) */
"no mailbox in address, only a phrase (%s%s)",
mbox
= add (buffer
, pers
);
if (route_addr (buffer
) == NOTOK
)
if (domain (buffer
) == NOTOK
)
(void) strcpy (err
, "extraneous semi-colon");
(void) sprintf (err
, "junk after local@domain (%s)",
case LX_SEMI
: /* no host */
if (last_lex
== LX_SEMI
&& glevel
-- <= 0) {
(void) strcpy (err
, "extraneous semi-colon");
(void) sprintf (err
, "missing mailbox (%s)", buffer
);
static int phrase (buffer
)
switch (my_lex (buffer
)) {
pers
= add (buffer
, add (" ", pers
));
static int route_addr (buffer
)
if (my_lex (buffer
) == LX_AT
) {
if (route (buffer
) == NOTOK
)
if (local_part (buffer
) == NOTOK
)
case LX_SEMI
: /* if in group */
case LX_RBRK
: /* no host */
(void) sprintf (err
, "no at-sign after local-part (%s)", buffer
);
static int local_part (buffer
)
switch (my_lex (buffer
)) {
mbox
= add (buffer
, mbox
);
(void) sprintf (err
, "no mailbox in local-part (%s)", buffer
);
switch (my_lex (buffer
)) {
mbox
= add (buffer
, mbox
);
static int domain (buffer
)
switch (my_lex (buffer
)) {
host
= add (buffer
, host
);
"no sub-domain in domain-part of address (%s)",
switch (my_lex (buffer
)) {
host
= add (buffer
, host
);
case LX_AT
: /* sigh (0) */
mbox
= add (host
, add ("%", mbox
));
static int route (buffer
)
switch (my_lex (buffer
)) {
path
= add (buffer
, path
);
"no sub-domain in domain-part of address (%s)",
switch (my_lex (buffer
)) {
path
= add (buffer
, path
);
switch (my_lex (buffer
)) {
path
= add (buffer
, path
);
"no at-sign found for next domain in route (%s)",
path
= add (buffer
, path
);
path
= add (buffer
, path
);
"no colon found to terminate route (%s)", buffer
);
static int my_lex (buffer
)
return (last_lex
= LX_END
);
return (last_lex
= LX_END
);
return (last_lex
= LX_ERR
);
return (last_lex
= LX_ERR
);
note
= note
? add (buffer
, add (" ", note
))
return (last_lex
= LX_ERR
);
return (last_lex
= LX_ERR
);
return (last_lex
= LX_QSTR
);
return (last_lex
= LX_ERR
);
return (last_lex
= LX_ERR
);
return (last_lex
= LX_DLIT
);
for (i
= 0; special
[i
].lx_chr
!= 0; i
++)
if (c
== special
[i
].lx_chr
)
return (last_lex
= special
[i
].lx_val
);
return (last_lex
= LX_ERR
);
for (i
= 0; special
[i
].lx_chr
!= 0; i
++)
if (c
== special
[i
].lx_chr
)
if (iscntrl (c
) || isspace (c
))
last_lex
= !gotat
|| cp
== NULL
|| index (cp
, '<') != NULL
static char buffer
[BUFSIZ
];
for (i
= 0; special
[i
].lx_chr
; i
++)
if (*cp
== special
[i
].lx_chr
) {
(void) sprintf (buffer
, "\"%s\"", p
);
if ((pp
= malloc ((unsigned) (len
= BUFSIZ
))) == NULL
)
for (ep
= (cp
= pp
) + len
- 2;;) {
if (cp
== pp
) /* end of headers, gobble it */
default: /* end of line */
case '\n': /* end of headers, save for next call */
case ' ': /* continue headers */
} /* fall into default case */
if ((dp
= realloc (pp
, (unsigned) (len
+= BUFSIZ
))) == NULL
) {
cp
+= dp
- pp
, ep
= (pp
= cp
) + len
- 2;