2 * mf.c -- mail filter subroutines
4 * This code is Copyright (c) 2002, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
17 static char *getcpy (char *);
18 static int isat (char *);
19 static int parse_address (void);
20 static int phrase (char *);
21 static int route_addr (char *);
22 static int local_part (char *);
23 static int domain (char *);
24 static int route (char *);
25 static int my_lex (char *);
35 * causes compiles to blow up because the symbol _cleanup
36 * is undefined where did this ever come from?
43 p = mh_xmalloc ((size_t) (strlen (s) + 2));
49 #define CHKADR 0 /* undertermined address style */
50 #define UNIXDR 1 /* UNIX-style address */
51 #define ARPADR 2 /* ARPAnet-style address */
57 return (strncmp (p, " AT ", 4)
58 && strncmp (p, " At ", 4)
59 && strncmp (p, " aT ", 4)
60 && strncmp (p, " at ", 4) ? FALSE : TRUE);
66 * getadrx() implements a partial 822-style address parser. The parser
67 * is neither complete nor correct. It does however recognize nearly all
68 * of the 822 address syntax. In addition it handles the majority of the
69 * 733 syntax as well. Most problems arise from trying to accomodate both.
71 * In terms of 822, the route-specification in
73 * "<" [route] local-part "@" domain ">"
75 * is parsed and returned unchanged. Multiple at-signs are compressed
76 * via source-routing. Recursive groups are not allowed as per the
79 * In terms of 733, " at " is recognized as equivalent to "@".
81 * In terms of both the parser will not complain about missing hosts.
85 * We should not allow addresses like
87 * Marshall T. Rose <MRose@UCI>
89 * but should insist on
91 * "Marshall T. Rose" <MRose@UCI>
93 * Unfortunately, a lot of mailers stupidly let people get away with this.
97 * We should not allow addresses like
101 * but should insist on
105 * Unfortunately, a lot of mailers stupidly let people's UAs get away with
110 * We should not allow addresses like
112 * @UCI:MRose@UCI-750a
114 * but should insist on
116 * Marshall Rose <@UCI:MRose@UCI-750a>
118 * Unfortunately, a lot of mailers stupidly do this.
142 static struct specials special[] = {
159 static int glevel = 0;
160 static int ingrp = 0;
161 static int last_lex = LX_END;
163 static char *dp = NULL;
164 static unsigned char *cp = NULL;
165 static unsigned char *ap = NULL;
166 static char *pers = NULL;
167 static char *mbox = NULL;
168 static char *host = NULL;
169 static char *path = NULL;
170 static char *grp = NULL;
171 static char *note = NULL;
172 static char err[BUFSIZ];
173 static char adr[BUFSIZ];
175 static struct adrx adrxs2;
179 getadrx (char *addrs)
182 register struct adrx *adrxp = &adrxs2;
196 pers = mbox = host = path = grp = note = NULL;
200 dp = cp = getcpy (addrs ? addrs : "");
202 } else if (cp == NULL) {
208 switch (parse_address ()) {
220 default: /* catch trailing comments */
245 while (isspace (*ap))
248 sprintf (adr, "%.*s", (int)(cp - ap), ap);
251 bp = adr + strlen (adr) - 1;
252 if (*bp == ',' || *bp == ';' || *bp == '\n')
261 adrxp->ingrp = ingrp;
263 adrxp->err = err[0] ? err : NULL;
276 switch (my_lex (buffer)) {
279 pers = getcpy (buffer);
284 strcpy (err, "extraneous semi-colon");
297 case LX_LBRK: /* sigh (2) */
300 case LX_AT: /* sigh (3) */
302 if (route_addr (buffer) == NOTOK)
304 return OK; /* why be choosy? */
307 sprintf (err, "illegal address construct (%s)", buffer);
311 switch (my_lex (buffer)) {
314 pers = add (buffer, add (" ", pers));
315 more_phrase: ; /* sigh (1) */
316 if (phrase (buffer) == NOTOK)
322 if (route_addr (buffer) == NOTOK)
324 if (last_lex == LX_RBRK)
326 sprintf (err, "missing right-bracket (%s)", buffer);
332 sprintf (err, "nested groups not allowed (%s)", pers);
335 grp = add (": ", pers);
341 switch (my_lex (buffer)) {
343 case LX_END: /* tsk, tsk */
352 return parse_address ();
356 case LX_DOT: /* sigh (1) */
357 pers = add (".", pers);
361 sprintf (err, "no mailbox in address, only a phrase (%s%s)", pers, buffer);
372 mbox = add (buffer, pers);
374 if (route_addr (buffer) == NOTOK)
382 if (domain (buffer) == NOTOK)
388 strcpy (err, "extraneous semi-colon");
396 sprintf (err, "junk after local@domain (%s)", buffer);
400 case LX_SEMI: /* no host */
404 if (last_lex == LX_SEMI && glevel-- <= 0) {
405 strcpy (err, "extraneous semi-colon");
413 sprintf (err, "missing mailbox (%s)", buffer);
420 phrase (char *buffer)
423 switch (my_lex (buffer)) {
426 pers = add (buffer, add (" ", pers));
436 route_addr (char *buffer)
438 register char *pp = cp;
440 if (my_lex (buffer) == LX_AT) {
441 if (route (buffer) == NOTOK)
447 if (local_part (buffer) == NOTOK)
452 return domain (buffer);
454 case LX_SEMI: /* if in group */
455 case LX_RBRK: /* no host */
461 sprintf (err, "no at-sign after local-part (%s)", buffer);
468 local_part (char *buffer)
473 switch (my_lex (buffer)) {
476 mbox = add (buffer, mbox);
480 sprintf (err, "no mailbox in local-part (%s)", buffer);
484 switch (my_lex (buffer)) {
486 mbox = add (buffer, mbox);
497 domain (char *buffer)
500 switch (my_lex (buffer)) {
503 host = add (buffer, host);
507 sprintf (err, "no sub-domain in domain-part of address (%s)", buffer);
511 switch (my_lex (buffer)) {
513 host = add (buffer, host);
516 case LX_AT: /* sigh (0) */
517 mbox = add (host, add ("%", mbox));
535 switch (my_lex (buffer)) {
538 path = add (buffer, path);
542 sprintf (err, "no sub-domain in domain-part of address (%s)", buffer);
545 switch (my_lex (buffer)) {
547 path = add (buffer, path);
549 switch (my_lex (buffer)) {
554 path = add (buffer, path);
558 sprintf (err, "no at-sign found for next domain in route (%s)",
565 case LX_AT: /* XXX */
567 path = add (buffer, path);
571 path = add (buffer, path);
575 sprintf (err, "no colon found to terminate route (%s)", buffer);
583 my_lex (char *buffer)
585 /* buffer should be at least BUFSIZ bytes long */
587 register unsigned char c;
591 * Add C to the buffer bp. After use of this macro *bp is guaranteed
592 * to be within the buffer.
597 if ((bp - buffer) == (BUFSIZ-1)) \
598 goto my_lex_buffull; \
604 return (last_lex = LX_END);
612 return (last_lex = LX_END);
621 return (last_lex = LX_ERR);
624 if ((c = *cp++) == 0) {
626 return (last_lex = LX_ERR);
639 note = note ? add (buffer, add (" ", note))
641 return my_lex (buffer);
652 return (last_lex = LX_ERR);
655 if ((c = *cp++) == 0) {
657 return (last_lex = LX_ERR);
665 return (last_lex = LX_QSTR);
675 return (last_lex = LX_ERR);
678 if ((c = *cp++) == 0) {
680 return (last_lex = LX_ERR);
688 return (last_lex = LX_DLIT);
694 for (i = 0; special[i].lx_chr != 0; i++)
695 if (c == special[i].lx_chr)
696 return (last_lex = special[i].lx_val);
699 return (last_lex = LX_ERR);
702 if ((c = *cp++) == 0)
704 for (i = 0; special[i].lx_chr != 0; i++)
705 if (c == special[i].lx_chr)
707 if (iscntrl (c) || isspace (c))
717 last_lex = !gotat || cp == NULL || strchr(cp, '<') != NULL
722 /* Out of buffer space. *bp is the last byte in the buffer */
724 return (last_lex = LX_ERR);
729 legal_person (char *p)
733 static char buffer[BUFSIZ];
737 for (cp = p; *cp; cp++)
738 for (i = 0; special[i].lx_chr; i++)
739 if (*cp == special[i].lx_chr) {
740 sprintf (buffer, "\"%s\"", p);