|
|
1.1 root 1: /* mf.c - mail filter subroutines */
2:
3: #include "mf.h"
4: #include <ctype.h>
5: #include <stdio.h>
6:
7: static int compress(), isat(), parse_address(), phrase(), route_addr(),
8: local_part(), domain(), route(), my_lex();
9:
10: /* */
11:
12: static char *
13: getcpy(s)
14: register char *s;
15: {
16: register char *p;
17:
18: if (!s) { _cleanup(); abort(); for(;;) pause();}
19: if ((p = malloc ((unsigned) (strlen (s) + 2))) != NULL)
20: (void) strcpy (p, s);
21: return p;
22: }
23:
24:
25: static char *
26: add(s1, s2)
27: register char *s1, *s2;
28: {
29: register char *p;
30:
31: if (s2 == NULL)
32: return getcpy (s1);
33:
34: if ((p = malloc ((unsigned) (strlen (s1) + strlen (s2) + 2))) != NULL)
35: (void) sprintf (p, "%s%s", s2, s1);
36: free (s2);
37: return p;
38: }
39:
40: /* */
41:
42: isfrom(string)
43: register char *string;
44: {
45: return (strncmp (string, "From ", 5) == 0
46: || strncmp (string, ">From ", 6) == 0);
47: }
48:
49:
50: lequal(a, b)
51: register char *a, *b;
52: {
53: for (; *a; a++, b++)
54: if (*b == NULL)
55: return FALSE;
56: else {
57: char c1 = islower (*a) ? toupper (*a) : *a;
58: char c2 = islower (*b) ? toupper (*b) : *b;
59: if (c1 != c2)
60: return FALSE;
61: }
62:
63: return (*b == NULL);
64: }
65:
66: /* */
67:
68: /*
69: *
70: * seekadrx() is tricky. We want to cover both UUCP-style and ARPA-style
71: * addresses, so for each list of addresses we see if we can find some
72: * character to give us a hint.
73: *
74: */
75:
76:
77: #define CHKADR 0 /* undertermined address style */
78: #define UNIXDR 1 /* UNIX-style address */
79: #define ARPADR 2 /* ARPAnet-style address */
80:
81:
82: static char *punctuators = ";<>.()[]";
83: static char *vp = NULL;
84: static char *tp = NULL;
85:
86: static struct adrx adrxs1;
87:
88: /* */
89:
90: struct adrx *
91: seekadrx(addrs)
92: register char *addrs;
93: {
94: static int state = CHKADR;
95: register char *cp;
96: register struct adrx *adrxp;
97:
98: if (state == CHKADR)
99: for (state = UNIXDR, cp = addrs; *cp; cp++)
100: if (index (punctuators, *cp)) {
101: state = ARPADR;
102: break;
103: }
104:
105: switch (state) {
106: case UNIXDR:
107: adrxp = uucpadrx (addrs);
108: break;
109:
110: case ARPADR:
111: default:
112: adrxp = getadrx (addrs);
113: break;
114: }
115:
116: if (adrxp == NULL)
117: state = CHKADR;
118:
119: return adrxp;
120: }
121:
122: /* */
123:
124: /*
125: *
126: * uucpadrx() implements a partial UUCP-style address parser. It's based
127: * on the UUCP notion that addresses are separated by spaces or commas.
128: *
129: */
130:
131:
132: struct adrx *
133: uucpadrx(addrs)
134: register char *addrs;
135: {
136: register char *cp,
137: *wp,
138: *xp,
139: *yp,
140: *zp;
141: register struct adrx *adrxp = &adrxs1;
142:
143: if (vp == NULL) {
144: vp = tp = getcpy (addrs);
145: compress (addrs, vp);
146: }
147: else
148: if (tp == NULL) {
149: free (vp);
150: vp = NULL;
151: return NULL;
152: }
153:
154: for (cp = tp; isspace (*cp); cp++)
155: continue;
156: if (*cp == NULL) {
157: free (vp);
158: vp = tp = NULL;
159: return NULL;
160: }
161:
162: /* */
163:
164: if ((wp = index (cp, ',')) == NULL)
165: if ((wp = index (cp, ' ')) != NULL) {
166: xp = wp;
167: while (isspace (*xp))
168: xp++;
169: if (*xp != NULL && isat (--xp)) {
170: yp = xp + 4;
171: while (isspace (*yp))
172: yp++;
173: if (*yp != NULL)
174: if ((zp = index (yp, ' ')) != NULL)
175: *zp = NULL, tp = ++zp;
176: else
177: tp = NULL;
178: else
179: *wp = NULL, tp = ++wp;
180: }
181: else
182: *wp = NULL, tp = ++wp;
183: }
184: else
185: tp = NULL;
186: else
187: *wp = NULL, tp = ++wp;
188:
189: if (adrxp -> text)
190: free (adrxp -> text);
191: adrxp -> text = getcpy (cp);
192: adrxp -> mbox = cp;
193: adrxp -> host = adrxp -> path = NULL;
194: if ((wp = rindex (cp, '@')) != NULL) {
195: *wp++ = NULL;
196: adrxp -> host = *wp ? wp : NULL;
197: }
198: else
199: for (wp = cp + strlen (cp) - 4; wp >= cp; wp--)
200: if (isat (wp)) {
201: *wp++ = NULL;
202: adrxp -> host = wp + 3;
203: }
204:
205: adrxp -> pers = adrxp -> grp = adrxp -> note = adrxp -> err = NULL;
206: adrxp -> ingrp = 0;
207:
208: return adrxp;
209: }
210:
211: /* */
212:
213: static int
214: compress(fp, tp)
215: register char *fp, *tp;
216: {
217: register char c,
218: *cp;
219:
220: for (c = ' ', cp = tp; (*tp = *fp++) != NULL;)
221: if (isspace (*tp)) {
222: if (c != ' ')
223: *tp++ = c = ' ';
224: }
225: else
226: c = *tp++;
227:
228: if (c == ' ' && cp < tp)
229: *--tp = NULL;
230: }
231:
232:
233: static int
234: isat(p)
235: register char *p;
236: {
237: return (strncmp (p, " AT ", 4)
238: && strncmp (p, " At ", 4)
239: && strncmp (p, " aT ", 4)
240: && strncmp (p, " at ", 4) ? FALSE : TRUE);
241: }
242:
243: /* */
244:
245: /*
246: *
247: * getadrx() implements a partial 822-style address parser. The parser
248: * is neither complete nor correct. It does however recognize nearly all
249: * of the 822 address syntax. In addition it handles the majority of the
250: * 733 syntax as well. Most problems arise from trying to accomodate both.
251: *
252: * In terms of 822, the route-specification in
253: *
254: * "<" [route] local-part "@" domain ">"
255: *
256: * is parsed and returned unchanged. Multiple at-signs are compressed
257: * via source-routing. Recursive groups are not allowed as per the
258: * standard.
259: *
260: * In terms of 733, " at " is recognized as equivalent to "@".
261: *
262: * In terms of both the parser will not complain about missing hosts.
263: *
264: * -----
265: *
266: * We should not allow addresses like
267: *
268: * Marshall T. Rose <MRose@UCI>
269: *
270: * but should insist on
271: *
272: * "Marshall T. Rose" <MRose@UCI>
273: *
274: * Unfortunately, a lot of mailers stupidly let people get away with this.
275: *
276: * -----
277: *
278: * We should not allow addresses like
279: *
280: * <MRose@UCI>
281: *
282: * but should insist on
283: *
284: * MRose@UCI
285: *
286: * Unfortunately, a lot of mailers stupidly let people's UAs get away with
287: * this.
288: *
289: * -----
290: *
291: * We should not allow addresses like
292: *
293: * @UCI:MRose@UCI-750a
294: *
295: * but should insist on
296: *
297: * Marshall Rose <@UCI:MRose@UCI-750a>
298: *
299: * Unfortunately, a lot of mailers stupidly do this.
300: *
301: */
302:
303: /* */
304:
305: #define QUOTE '\\'
306:
307: #define LX_END 0
308: #define LX_ERR 1
309: #define LX_ATOM 2
310: #define LX_QSTR 3
311: #define LX_DLIT 4
312: #define LX_SEMI 5
313: #define LX_COMA 6
314: #define LX_LBRK 7
315: #define LX_RBRK 8
316: #define LX_COLN 9
317: #define LX_DOT 10
318: #define LX_AT 11
319:
320: static struct {
321: char lx_chr;
322: int lx_val;
323: } special[] = {
324: ';', LX_SEMI,
325: ',', LX_COMA,
326: '<', LX_LBRK,
327: '>', LX_RBRK,
328: ':', LX_COLN,
329: '.', LX_DOT,
330: '@', LX_AT,
331: '(', LX_ERR,
332: ')', LX_ERR,
333: QUOTE, LX_ERR,
334: '"', LX_ERR,
335: '[', LX_ERR,
336: ']', LX_ERR,
337: NULL, NULL
338: };
339:
340: /* */
341:
342: static int glevel = 0;
343: static int ingrp = 0;
344: static int last_lex = LX_END;
345:
346: static char *dp = NULL;
347: static char *cp = NULL;
348: static char *ap = NULL;
349: static char *pers = NULL;
350: static char *mbox = NULL;
351: static char *host = NULL;
352: static char *path = NULL;
353: static char *grp = NULL;
354: static char *note = NULL;
355: static char err[BUFSIZ];
356: static char adr[BUFSIZ];
357:
358: static struct adrx adrxs2;
359:
360: /* */
361:
362: struct adrx *
363: getadrx(addrs)
364: register char *addrs;
365: {
366: register char *bp;
367: register struct adrx *adrxp = &adrxs2;
368:
369: if (pers)
370: free (pers);
371: if (mbox)
372: free (mbox);
373: if (host)
374: free (host);
375: if (path)
376: free (path);
377: if (grp)
378: free (grp);
379: if (note)
380: free (note);
381: pers = mbox = host = path = grp = note = NULL;
382: err[0] = NULL;
383:
384: if (dp == NULL) {
385: dp = cp = getcpy (addrs ? addrs : "");
386: glevel = 0;
387: }
388: else
389: if (cp == NULL) {
390: free (dp);
391: dp = NULL;
392: return NULL;
393: }
394:
395: switch (parse_address ()) {
396: case DONE:
397: free (dp);
398: dp = cp = NULL;
399: return NULL;
400:
401: case OK:
402: switch (last_lex) {
403: case LX_COMA:
404: case LX_END:
405: break;
406:
407: default: /* catch trailing comments */
408: bp = cp;
409: (void) my_lex (adr);
410: cp = bp;
411: break;
412: }
413: break;
414:
415: default:
416: break;
417: }
418:
419: if (err[0])
420: for (;;) {
421: switch (last_lex) {
422: case LX_COMA:
423: case LX_END:
424: break;
425:
426: default:
427: (void) my_lex (adr);
428: continue;
429: }
430: break;
431: }
432: while (isspace (*ap))
433: ap++;
434: if (cp)
435: (void) sprintf (adr, "%.*s", cp - ap, ap);
436: else
437: (void) strcpy (adr, ap);
438: bp = adr + strlen (adr) - 1;
439: if (*bp == ',' || *bp == ';' || *bp == '\n')
440: *bp = NULL;
441:
442: /* */
443:
444: adrxp -> text = adr;
445: adrxp -> pers = pers;
446: adrxp -> mbox = mbox;
447: adrxp -> host = host;
448: adrxp -> path = path;
449: adrxp -> grp = grp;
450: adrxp -> ingrp = ingrp;
451: adrxp -> note = note;
452: adrxp -> err = err[0] ? err : NULL;
453:
454: return adrxp;
455: }
456:
457: /* */
458:
459: static int
460: parse_address()
461: {
462: char buffer[BUFSIZ];
463:
464: again: ;
465: ap = cp;
466: switch (my_lex (buffer)) {
467: case LX_ATOM:
468: case LX_QSTR:
469: pers = getcpy (buffer);
470: break;
471:
472: case LX_SEMI:
473: if (glevel-- <= 0) {
474: (void) strcpy (err, "extraneous semi-colon");
475: return NOTOK;
476: }
477: case LX_COMA:
478: if (note) {
479: free (note);
480: note = NULL;
481: }
482: goto again;
483:
484: case LX_END:
485: return DONE;
486:
487: case LX_LBRK: /* sigh (2) */
488: goto get_addr;
489:
490: case LX_AT: /* sigh (3) */
491: cp = ap;
492: if (route_addr (buffer) == NOTOK)
493: return NOTOK;
494: return OK; /* why be choosy? */
495:
496: default:
497: (void) sprintf (err, "illegal address construct (%s)", buffer);
498: return NOTOK;
499: }
500:
501: /* */
502:
503: switch (my_lex (buffer)) {
504: case LX_ATOM:
505: case LX_QSTR:
506: pers = add (buffer, add (" ", pers));
507: more_phrase: ; /* sigh (1) */
508: if (phrase (buffer) == NOTOK)
509: return NOTOK;
510:
511: switch (last_lex) {
512: case LX_LBRK:
513: get_addr: ;
514: if (route_addr (buffer) == NOTOK)
515: return NOTOK;
516: if (last_lex == LX_RBRK)
517: return OK;
518: (void) sprintf (err, "missing right-bracket (%s)", buffer);
519: return NOTOK;
520:
521: case LX_COLN:
522: get_group: ;
523: if (glevel++ > 0) {
524: (void) sprintf (err, "nested groups not allowed (%s)",
525: pers);
526: return NOTOK;
527: }
528: grp = add (": ", pers);
529: pers = NULL;
530: {
531: char *pp = cp;
532:
533: for (;;)
534: switch (my_lex (buffer)) {
535: case LX_SEMI:
536: case LX_END: /* tsk, tsk */
537: glevel--;
538: return OK;
539:
540: case LX_COMA:
541: continue;
542:
543: default:
544: cp = pp;
545: return parse_address ();
546: }
547: }
548:
549: case LX_DOT: /* sigh (1) */
550: pers = add (".", pers);
551: goto more_phrase;
552:
553: default:
554: (void) sprintf (err,
555: "no mailbox in address, only a phrase (%s%s)",
556: pers, buffer);
557: return NOTOK;
558: }
559:
560: /* */
561:
562: case LX_LBRK:
563: goto get_addr;
564:
565: case LX_COLN:
566: goto get_group;
567:
568: case LX_DOT:
569: mbox = add (buffer, pers);
570: pers = NULL;
571: if (route_addr (buffer) == NOTOK)
572: return NOTOK;
573: goto check_end;
574:
575: case LX_AT:
576: ingrp = glevel;
577: mbox = pers;
578: pers = NULL;
579: if (domain (buffer) == NOTOK)
580: return NOTOK;
581: check_end: ;
582: switch (last_lex) {
583: case LX_SEMI:
584: if (glevel-- <= 0) {
585: (void) strcpy (err, "extraneous semi-colon");
586: return NOTOK;
587: }
588: case LX_COMA:
589: case LX_END:
590: return OK;
591:
592: default:
593: (void) sprintf (err, "junk after local@domain (%s)",
594: buffer);
595: return NOTOK;
596: }
597:
598: case LX_SEMI: /* no host */
599: case LX_COMA:
600: case LX_END:
601: ingrp = glevel;
602: if (last_lex == LX_SEMI && glevel-- <= 0) {
603: (void) strcpy (err, "extraneous semi-colon");
604: return NOTOK;
605: }
606: mbox = pers;
607: pers = NULL;
608: return OK;
609:
610: default:
611: (void) sprintf (err, "missing mailbox (%s)", buffer);
612: return NOTOK;
613: }
614: }
615:
616: /* */
617:
618: static int
619: phrase(buffer)
620: register char *buffer;
621: {
622: for (;;)
623: switch (my_lex (buffer)) {
624: case LX_ATOM:
625: case LX_QSTR:
626: pers = add (buffer, add (" ", pers));
627: continue;
628:
629: default:
630: return OK;
631: }
632: }
633:
634: /* */
635:
636: static int
637: route_addr(buffer)
638: register char *buffer;
639: {
640: register char *pp = cp;
641:
642: if (my_lex (buffer) == LX_AT) {
643: if (route (buffer) == NOTOK)
644: return NOTOK;
645: }
646: else
647: cp = pp;
648:
649: if (local_part (buffer) == NOTOK)
650: return NOTOK;
651:
652: switch (last_lex) {
653: case LX_AT:
654: return domain (buffer);
655:
656: case LX_RBRK: /* no host */
657: case LX_COMA:
658: case LX_END:
659: return OK;
660:
661: default:
662: (void) sprintf (err, "no at-sign after local-part (%s)", buffer);
663: return NOTOK;
664: }
665: }
666:
667: /* */
668:
669: static int
670: local_part(buffer)
671: register char *buffer;
672: {
673: ingrp = glevel;
674:
675: for (;;) {
676: switch (my_lex (buffer)) {
677: case LX_ATOM:
678: case LX_QSTR:
679: mbox = add (buffer, mbox);
680: break;
681:
682: default:
683: (void) sprintf (err, "no mailbox in local-part (%s)", buffer);
684: return NOTOK;
685: }
686:
687: switch (my_lex (buffer)) {
688: case LX_DOT:
689: mbox = add (buffer, mbox);
690: continue;
691:
692: default:
693: return OK;
694: }
695: }
696: }
697:
698: /* */
699:
700: static int
701: domain(buffer)
702: register char *buffer;
703: {
704: for (;;) {
705: switch (my_lex (buffer)) {
706: case LX_ATOM:
707: case LX_DLIT:
708: host = add (buffer, host);
709: break;
710:
711: default:
712: (void) sprintf (err,
713: "no sub-domain in domain-part of address (%s)",
714: buffer);
715: return NOTOK;
716: }
717:
718: switch (my_lex (buffer)) {
719: case LX_DOT:
720: host = add (buffer, host);
721: continue;
722:
723: case LX_AT: /* sigh (0) */
724: mbox = add (host, add ("%", mbox));
725: free (host);
726: host = NULL;
727: continue;
728:
729: default:
730: return OK;
731: }
732: }
733: }
734:
735: /* */
736:
737: static int
738: route(buffer)
739: register char *buffer;
740: {
741: path = getcpy ("@");
742:
743: for (;;) {
744: switch (my_lex (buffer)) {
745: case LX_ATOM:
746: case LX_DLIT:
747: path = add (buffer, path);
748: break;
749:
750: default:
751: (void) sprintf (err,
752: "no sub-domain in domain-part of address (%s)",
753: buffer);
754: return NOTOK;
755: }
756: switch (my_lex (buffer)) {
757: case LX_COMA:
758: path = add (buffer, path);
759: for (;;) {
760: switch (my_lex (buffer)) {
761: case LX_COMA:
762: continue;
763:
764: case LX_AT:
765: path = add (buffer, path);
766: break;
767:
768: default:
769: (void) sprintf (err,
770: "no at-sign found for next domain in route (%s)",
771: buffer);
772: }
773: break;
774: }
775: continue;
776:
777: case LX_AT: /* XXX */
778: case LX_DOT:
779: path = add (buffer, path);
780: continue;
781:
782: case LX_COLN:
783: path = add (buffer, path);
784: return OK;
785:
786: default:
787: (void) sprintf (err,
788: "no colon found to terminate route (%s)", buffer);
789: return NOTOK;
790: }
791: }
792: }
793:
794: /* */
795:
796: static int
797: my_lex(buffer)
798: register char *buffer;
799: {
800: int i;
801: register char c,
802: *bp;
803:
804: bp = buffer;
805: *bp = NULL;
806: if (!cp)
807: return (last_lex = LX_END);
808:
809: c = *cp++;
810: while (isspace (c))
811: c = *cp++;
812: if (c == NULL) {
813: cp = NULL;
814: return (last_lex = LX_END);
815: }
816:
817: if (c == '(')
818: for (*bp++ = c, i = 0;;)
819: switch (c = *cp++) {
820: case NULL:
821: cp = NULL;
822: return (last_lex = LX_ERR);
823: case QUOTE:
824: *bp++ = c;
825: if ((c = *cp++) == NULL) {
826: cp = NULL;
827: return (last_lex = LX_ERR);
828: }
829: *bp++ = c;
830: continue;
831: case '(':
832: i++;
833: default:
834: *bp++ = c;
835: continue;
836: case ')':
837: *bp++ = c;
838: if (--i < 0) {
839: *bp = NULL;
840: note = note ? add (buffer, add (" ", note))
841: : getcpy (buffer);
842: return my_lex (buffer);
843: }
844: }
845:
846: /* */
847:
848: if (c == '"')
849: for (*bp++ = c;;)
850: switch (c = *cp++) {
851: case NULL:
852: cp = NULL;
853: return (last_lex = LX_ERR);
854: case QUOTE:
855: *bp++ = c;
856: if ((c = *cp++) == NULL) {
857: cp = NULL;
858: return (last_lex = LX_ERR);
859: }
860: default:
861: *bp++ = c;
862: continue;
863: case '"':
864: *bp++ = c;
865: *bp = NULL;
866: return (last_lex = LX_QSTR);
867: }
868:
869: if (c == '[')
870: for (*bp++ = c;;)
871: switch (c = *cp++) {
872: case NULL:
873: cp = NULL;
874: return (last_lex = LX_ERR);
875: case QUOTE:
876: *bp++ = c;
877: if ((c = *cp++) == NULL) {
878: cp = NULL;
879: return (last_lex = LX_ERR);
880: }
881: default:
882: *bp++ = c;
883: continue;
884: case ']':
885: *bp++ = c;
886: *bp = NULL;
887: return (last_lex = LX_DLIT);
888: }
889:
890: /* */
891:
892: *bp++ = c;
893: *bp = NULL;
894: for (i = 0; special[i].lx_chr != NULL; i++)
895: if (c == special[i].lx_chr)
896: return (last_lex = special[i].lx_val);
897:
898: if (iscntrl (c))
899: return (last_lex = LX_ERR);
900:
901: for (;;) {
902: if ((c = *cp++) == NULL)
903: break;
904: for (i = 0; special[i].lx_chr != NULL; i++)
905: if (c == special[i].lx_chr)
906: goto got_atom;
907: if (iscntrl (c) || isspace (c))
908: break;
909: *bp++ = c;
910: }
911: got_atom: ;
912: if (c == NULL)
913: cp = NULL;
914: else
915: cp--;
916: *bp = NULL;
917: last_lex = !lequal (buffer, "at") || cp == NULL || index (cp, '<') != NULL
918: ? LX_ATOM : LX_AT;
919: return last_lex;
920: }
921:
922: /* */
923:
924: char *
925: legal_person(p)
926: register char *p;
927: {
928: int i;
929: register char *cp;
930: static char buffer[BUFSIZ];
931:
932: if (*p == '"')
933: return p;
934: for (cp = p; *cp; cp++)
935: for (i = 0; special[i].lx_chr; i++)
936: if (*cp == special[i].lx_chr) {
937: (void) sprintf (buffer, "\"%s\"", p);
938: return buffer;
939: }
940:
941: return p;
942: }
943:
944: /* */
945:
946: mfgets(in, bp)
947: register FILE *in;
948: register char **bp;
949: {
950: int i;
951: register char *cp,
952: *dp,
953: *ep;
954: static int len = 0;
955: static char *pp = NULL;
956:
957: if (pp == NULL)
958: if ((pp = malloc ((unsigned) (len = BUFSIZ))) == NULL)
959: return NOTOK;
960:
961: for (ep = (cp = pp) + len - 2;;) {
962: switch (i = getc (in)) {
963: case EOF:
964: eol: ;
965: if (cp != pp) {
966: *cp = NULL;
967: *bp = pp;
968: return OK;
969: }
970: eoh: ;
971: *bp = NULL;
972: free (pp);
973: pp = NULL;
974: return DONE;
975:
976: case NULL:
977: continue;
978:
979: case '\n':
980: if (cp == pp) /* end of headers, gobble it */
981: goto eoh;
982: switch (i = getc (in)) {
983: default: /* end of line */
984: case '\n': /* end of headers, save for next call */
985: (void) ungetc (i, in);
986: goto eol;
987:
988: case ' ': /* continue headers */
989: case '\t':
990: *cp++ = '\n';
991: break;
992: } /* fall into default case */
993:
994: default:
995: *cp++ = i;
996: break;
997: }
998: if (cp >= ep)
999: if ((dp = realloc (pp, (unsigned) (len += BUFSIZ))) == NULL) {
1000: free (pp);
1001: pp = NULL;
1002: return NOTOK;
1003: }
1004: else
1005: cp += dp - pp, ep = (pp = cp) + len - 2;
1006: }
1007: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.