Remove not used code (JLR define)
[mmh] / sbr / mf.c
1 /*
2 ** mf.c -- mail filter subroutines
3 **
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.
7 */
8
9 #include <h/mf.h>
10 #include <ctype.h>
11 #include <stdio.h>
12 #include <h/utils.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <string.h>
16
17 /*
18 ** static prototypes
19 */
20 static char *getcpy(char *);
21 static int parse_address(void);
22 static int phrase(char *);
23 static int route_addr(char *);
24 static int local_part(char *);
25 static int domain(char *);
26 static int route(char *);
27 static int my_lex(char *);
28
29
30 static char *
31 getcpy(char *s)
32 {
33         char *p;
34
35         if (!s) {
36                 /*
37                 ** causes compiles to blow up because the symbol _cleanup
38                 ** is undefined where did this ever come from?
39                 */
40                 /* _cleanup(); */
41                 abort();
42                 for(;;)
43                         pause();
44         }
45         p = mh_xcalloc(strlen(s) + 2, sizeof(char));
46         strcpy(p, s);
47         return p;
48 }
49
50
51 /*
52 **
53 ** getadrx() implements a partial 822-style address parser.  The parser
54 ** is neither complete nor correct.  It does however recognize nearly all
55 ** of the 822 address syntax.
56 ** Historically, it handled the majority (and still handles parts) of the
57 ** 733 syntax as well.  Most problems arise from trying to accommodate both.
58 **
59 ** In terms of 822, the route-specification in
60 **
61 **     "<" [route] local-part "@" domain ">"
62 **
63 ** is parsed and returned unchanged.  Multiple at-signs are compressed
64 ** via source-routing.  Recursive groups are not allowed as per the
65 ** standard.
66 **
67 ** In terms of both the parser will not complain about missing hosts.
68 **
69 ** -----
70 **
71 ** We should not allow addresses like
72 **
73 **     Marshall T. Rose <MRose@UCI>
74 **
75 ** but should insist on
76 **
77 **     "Marshall T. Rose" <MRose@UCI>
78 **
79 ** Unfortunately, a lot of mailers stupidly let people get away with this.
80 **
81 ** -----
82 **
83 ** We should not allow addresses like
84 **
85 **     <MRose@UCI>
86 **
87 ** but should insist on
88 **
89 **     MRose@UCI
90 **
91 ** Unfortunately, a lot of mailers stupidly let people's UAs get away with
92 ** this.
93 **
94 ** -----
95 **
96 ** We should not allow addresses like
97 **
98 **     @UCI:MRose@UCI-750a
99 **
100 ** but should insist on
101 **
102 **     Marshall Rose <@UCI:MRose@UCI-750a>
103 **
104 ** Unfortunately, a lot of mailers stupidly do this.
105 **
106 */
107
108 #define QUOTE  '\\'
109
110 #define LX_END   0
111 #define LX_ERR   1
112 #define LX_ATOM  2
113 #define LX_QSTR  3
114 #define LX_DLIT  4
115 #define LX_SEMI  5
116 #define LX_COMA  6
117 #define LX_LBRK  7
118 #define LX_RBRK  8
119 #define LX_COLN  9
120 #define LX_DOT  10
121 #define LX_AT   11
122
123 struct specials {
124         char lx_chr;
125         int  lx_val;
126 };
127
128 static struct specials special[] = {
129         { ';',   LX_SEMI },
130         { ',',   LX_COMA },
131         { '<',   LX_LBRK },
132         { '>',   LX_RBRK },
133         { ':',   LX_COLN },
134         { '.',   LX_DOT },
135         { '@',   LX_AT },
136         { '(',   LX_ERR },
137         { ')',   LX_ERR },
138         { QUOTE, LX_ERR },
139         { '"',   LX_ERR },
140         { '[',   LX_ERR },
141         { ']',   LX_ERR },
142         { 0,     0 }
143 };
144
145 static int glevel = 0;
146 static int ingrp = 0;
147 static int last_lex = LX_END;
148
149 static char *dp = NULL;
150 static unsigned char *cp = NULL;
151 static unsigned char *ap = NULL;
152 static char *pers = NULL;
153 static char *mbox = NULL;
154 static char *host = NULL;
155 static char *path = NULL;
156 static char *grp = NULL;
157 static char *note = NULL;
158 static char err[BUFSIZ];
159 static char adr[BUFSIZ];
160
161 static struct adrx  adrxs2;
162
163
164 struct adrx *
165 getadrx(char *addrs)
166 {
167         char *bp;
168         struct adrx *adrxp = &adrxs2;
169
170         if (pers)
171                 mh_free0(&pers);
172         if (mbox)
173                 mh_free0(&mbox);
174         if (host)
175                 mh_free0(&host);
176         if (path)
177                 mh_free0(&path);
178         if (grp)
179                 mh_free0(&grp);
180         if (note)
181                 mh_free0(&note);
182         err[0] = 0;
183
184         if (dp == NULL) {
185                 dp = cp = getcpy(addrs ? addrs : "");
186                 glevel = 0;
187         } else if (cp == NULL) {
188                 mh_free0(&dp);
189                 return NULL;
190         }
191
192         switch (parse_address()) {
193         case DONE:
194                 mh_free0(&dp);
195                 cp = NULL;
196                 return NULL;
197
198         case OK:
199                 switch (last_lex) {
200                 case LX_COMA:
201                 case LX_END:
202                         break;
203
204                 default:  /* catch trailing comments */
205                         bp = cp;
206                         my_lex(adr);
207                         cp = bp;
208                         break;
209                 }
210                 break;
211
212         default:
213                 break;
214         }
215
216         if (err[0])
217                 for (;;) {
218                         switch (last_lex) {
219                         case LX_COMA:
220                         case LX_END:
221                                 break;
222
223                         default:
224                                 my_lex(adr);
225                                 continue;
226                         }
227                         break;
228                 }
229         while (isspace(*ap))
230                 ap++;
231         if (cp)
232                 sprintf(adr, "%.*s", (int)(cp - ap), ap);
233         else
234                 strcpy(adr, ap);
235         bp = adr + strlen(adr) - 1;
236         if (*bp == ',' || *bp == ';' || *bp == '\n')
237                 *bp = 0;
238
239         adrxp->text = adr;
240         adrxp->pers = pers;
241         adrxp->mbox = mbox;
242         adrxp->host = host;
243         adrxp->path = path;
244         adrxp->grp = grp;
245         adrxp->ingrp = ingrp;
246         adrxp->note = note;
247         adrxp->err = err[0] ? err : NULL;
248
249         return adrxp;
250 }
251
252
253 static int
254 parse_address(void)
255 {
256         char buffer[BUFSIZ];
257
258 again: ;
259         ap = cp;
260         switch (my_lex(buffer)) {
261         case LX_ATOM:
262         case LX_QSTR:
263                 pers = getcpy(buffer);
264                 break;
265
266         case LX_SEMI:
267                 if (glevel-- <= 0) {
268                         strcpy(err, "extraneous semi-colon");
269                         return NOTOK;
270                 }
271         case LX_COMA:
272                 if (note) {
273                         mh_free0(&note);
274                 }
275                 goto again;
276
277         case LX_END:
278                 return DONE;
279
280         case LX_LBRK:  /* sigh (2) */
281                 goto get_addr;
282
283         case LX_AT:  /* sigh (3) */
284                 cp = ap;
285                 if (route_addr(buffer) == NOTOK)
286                         return NOTOK;
287                 return OK;  /* why be choosy? */
288
289         default:
290                 sprintf(err, "illegal address construct (%s)", buffer);
291                 return NOTOK;
292         }
293
294         switch (my_lex(buffer)) {
295         case LX_ATOM:
296         case LX_QSTR:
297                 pers = add(buffer, add(" ", pers));
298 more_phrase: ;  /* sigh (1) */
299                 if (phrase(buffer) == NOTOK)
300                         return NOTOK;
301
302                 switch (last_lex) {
303                 case LX_LBRK:
304 get_addr: ;
305                         if (route_addr(buffer) == NOTOK)
306                                 return NOTOK;
307                         if (last_lex == LX_RBRK)
308                                 return OK;
309                         sprintf(err, "missing right-bracket (%s)", buffer);
310                         return NOTOK;
311
312                 case LX_COLN:
313 get_group: ;
314                         if (glevel++ > 0) {
315                                 sprintf(err, "nested groups not allowed (%s)", pers);
316                                 return NOTOK;
317                         }
318                         grp = add(": ", pers);
319                         pers = NULL;
320                         {
321                                 char   *pp = cp;
322
323                                 for (;;)
324                                         switch (my_lex(buffer)) {
325                                         case LX_SEMI:
326                                         case LX_END: /* tsk, tsk */
327                                                 glevel--;
328                                                 return OK;
329
330                                         case LX_COMA:
331                                                 continue;
332
333                                         default:
334                                                 cp = pp;
335                                                 return parse_address();
336                                         }
337                         }
338
339                 case LX_DOT:  /* sigh (1) */
340                         pers = add(".", pers);
341                         goto more_phrase;
342
343                 default:
344                         sprintf(err, "no mailbox in address, only a phrase (%s%s)", pers, buffer);
345                         return NOTOK;
346                 }
347
348         case LX_LBRK:
349                 goto get_addr;
350
351         case LX_COLN:
352                 goto get_group;
353
354         case LX_DOT:
355                 mbox = add(buffer, pers);
356                 pers = NULL;
357                 if (route_addr(buffer) == NOTOK)
358                         return NOTOK;
359                 goto check_end;
360
361         case LX_AT:
362                 ingrp = glevel;
363                 mbox = pers;
364                 pers = NULL;
365                 if (domain(buffer) == NOTOK)
366                         return NOTOK;
367 check_end: ;
368                 switch (last_lex) {
369                 case LX_SEMI:
370                         if (glevel-- <= 0) {
371                                 strcpy(err, "extraneous semi-colon");
372                                 return NOTOK;
373                         }
374                 case LX_COMA:
375                 case LX_END:
376                         return OK;
377
378                 default:
379                         sprintf(err, "junk after local@domain (%s)", buffer);
380                         return NOTOK;
381                 }
382
383         case LX_SEMI:  /* no host */
384         case LX_COMA:
385         case LX_END:
386                 ingrp = glevel;
387                 if (last_lex == LX_SEMI && glevel-- <= 0) {
388                         strcpy(err, "extraneous semi-colon");
389                         return NOTOK;
390                 }
391                 mbox = pers;
392                 pers = NULL;
393                 return OK;
394
395         default:
396                 sprintf(err, "missing mailbox (%s)", buffer);
397                 return NOTOK;
398         }
399 }
400
401
402 static int
403 phrase(char *buffer)
404 {
405         for (;;)
406                 switch (my_lex(buffer)) {
407                 case LX_ATOM:
408                 case LX_QSTR:
409                         pers = add(buffer, add(" ", pers));
410                         continue;
411
412                 default:
413                         return OK;
414                 }
415 }
416
417
418 static int
419 route_addr(char *buffer)
420 {
421         char *pp = cp;
422
423         if (my_lex(buffer) == LX_AT) {
424                 if (route(buffer) == NOTOK)
425                         return NOTOK;
426         }
427         else
428                 cp = pp;
429
430         if (local_part(buffer) == NOTOK)
431                 return NOTOK;
432
433         switch (last_lex) {
434         case LX_AT:
435                 return domain(buffer);
436
437         case LX_SEMI:  /* if in group */
438         case LX_RBRK:  /* no host */
439         case LX_COMA:
440         case LX_END:
441                 return OK;
442
443         default:
444                 sprintf(err, "no at-sign after local-part (%s)", buffer);
445                 return NOTOK;
446         }
447 }
448
449
450 static int
451 local_part(char *buffer)
452 {
453         ingrp = glevel;
454
455         for (;;) {
456                 switch (my_lex(buffer)) {
457                 case LX_ATOM:
458                 case LX_QSTR:
459                         mbox = add(buffer, mbox);
460                         break;
461
462                 default:
463                         sprintf(err, "no mailbox in local-part (%s)", buffer);
464                         return NOTOK;
465                 }
466
467                 switch (my_lex(buffer)) {
468                 case LX_DOT:
469                         mbox = add(buffer, mbox);
470                         continue;
471
472                 default:
473                         return OK;
474                 }
475         }
476 }
477
478
479 static int
480 domain(char *buffer)
481 {
482         for (;;) {
483                 switch (my_lex(buffer)) {
484                 case LX_ATOM:
485                 case LX_DLIT:
486                         host = add(buffer, host);
487                         break;
488
489                 default:
490                         sprintf(err, "no sub-domain in domain-part of address (%s)", buffer);
491                         return NOTOK;
492                 }
493
494                 switch (my_lex(buffer)) {
495                 case LX_DOT:
496                         host = add(buffer, host);
497                         continue;
498
499                 case LX_AT:  /* sigh (0) */
500                         mbox = add(host, add("%", mbox));
501                         mh_free0(&host);
502                         continue;
503
504                 default:
505                         return OK;
506                 }
507         }
508 }
509
510
511 static int
512 route(char *buffer)
513 {
514         path = getcpy("@");
515
516         for (;;) {
517                 switch (my_lex(buffer)) {
518                 case LX_ATOM:
519                 case LX_DLIT:
520                         path = add(buffer, path);
521                         break;
522
523                 default:
524                         sprintf(err, "no sub-domain in domain-part of address (%s)", buffer);
525                         return NOTOK;
526                 }
527                 switch (my_lex(buffer)) {
528                 case LX_COMA:
529                         path = add(buffer, path);
530                         for (;;) {
531                                 switch (my_lex(buffer)) {
532                                 case LX_COMA:
533                                         continue;
534
535                                 case LX_AT:
536                                         path = add(buffer, path);
537                                         break;
538
539                                 default:
540                                         sprintf(err, "no at-sign found for next domain in route (%s)",
541                                                         buffer);
542                                 }
543                                 break;
544                         }
545                         continue;
546
547                 case LX_AT:  /* XXX */
548                 case LX_DOT:
549                         path = add(buffer, path);
550                         continue;
551
552                 case LX_COLN:
553                         path = add(buffer, path);
554                         return OK;
555
556                 default:
557                         sprintf(err, "no colon found to terminate route (%s)", buffer);
558                         return NOTOK;
559                 }
560         }
561 }
562
563
564 static int
565 my_lex(char *buffer)
566 {
567         /* buffer should be at least BUFSIZ bytes long */
568         int i;
569         unsigned char c;
570         char *bp;
571
572         /*
573         ** Add C to the buffer bp. After use of this macro *bp is guaranteed
574         ** to be within the buffer.
575         */
576 #define ADDCHR(C)  \
577         do { \
578                 *bp++ = (C); \
579                 if ((bp - buffer) == (BUFSIZ-1)) \
580                         goto my_lex_buffull; \
581         } while (0)
582
583         bp = buffer;
584         *bp = 0;
585         if (!cp)
586                 return (last_lex = LX_END);
587
588         c = *cp++;
589         while (isspace(c))
590                 c = *cp++;
591         if (c == 0) {
592                 cp = NULL;
593                 return (last_lex = LX_END);
594         }
595
596         if (c == '(') {
597                 ADDCHR(c);
598                 for (i = 0;;)
599                         switch (c = *cp++) {
600                         case 0:
601                                 cp = NULL;
602                                 return (last_lex = LX_ERR);
603                         case QUOTE:
604                                 ADDCHR(c);
605                                 if ((c = *cp++) == 0) {
606                                         cp = NULL;
607                                         return (last_lex = LX_ERR);
608                                 }
609                                 ADDCHR(c);
610                                 continue;
611                         case '(':
612                                 i++;
613                         default:
614                                 ADDCHR(c);
615                                 continue;
616                         case ')':
617                                 ADDCHR(c);
618                                 if (--i < 0) {
619                                         *bp = 0;
620                                         note = note ? add(buffer, add(" ", note)) : getcpy(buffer);
621                                         return my_lex(buffer);
622                                 }
623                         }
624         }
625
626         if (c == '"') {
627                 ADDCHR(c);
628                 for (;;)
629                         switch (c = *cp++) {
630                         case 0:
631                                 cp = NULL;
632                                 return (last_lex = LX_ERR);
633                         case QUOTE:
634                                 ADDCHR(c);
635                                 if ((c = *cp++) == 0) {
636                                         cp = NULL;
637                                         return (last_lex = LX_ERR);
638                                 }
639                         default:
640                                 ADDCHR(c);
641                                 continue;
642                         case '"':
643                                 ADDCHR(c);
644                                 *bp = 0;
645                                 return (last_lex = LX_QSTR);
646                         }
647         }
648
649         if (c == '[') {
650                 ADDCHR(c);
651                 for (;;)
652                         switch (c = *cp++) {
653                         case 0:
654                                 cp = NULL;
655                                 return (last_lex = LX_ERR);
656                         case QUOTE:
657                                 ADDCHR(c);
658                                 if ((c = *cp++) == 0) {
659                                         cp = NULL;
660                                         return (last_lex = LX_ERR);
661                                 }
662                         default:
663                                 ADDCHR(c);
664                                 continue;
665                         case ']':
666                                 ADDCHR(c);
667                                 *bp = 0;
668                                 return (last_lex = LX_DLIT);
669                         }
670         }
671
672         ADDCHR(c);
673         *bp = 0;
674         for (i = 0; special[i].lx_chr != 0; i++)
675                 if (c == special[i].lx_chr)
676                         return (last_lex = special[i].lx_val);
677
678         if (iscntrl(c))
679                 return (last_lex = LX_ERR);
680
681         for (;;) {
682                 if ((c = *cp++) == 0)
683                         break;
684                 for (i = 0; special[i].lx_chr != 0; i++)
685                         if (c == special[i].lx_chr)
686                                 goto got_atom;
687                 if (iscntrl(c) || isspace(c))
688                         break;
689                 ADDCHR(c);
690         }
691 got_atom: ;
692         if (c == 0)
693                 cp = NULL;
694         else
695                 cp--;
696         *bp = 0;
697         return LX_ATOM;
698
699  my_lex_buffull:
700         /* Out of buffer space. *bp is the last byte in the buffer */
701         *bp = 0;
702         return (last_lex = LX_ERR);
703 }
704
705
706 char *
707 legal_person(char *p)
708 {
709         int i;
710         char *cp;
711         static char buffer[BUFSIZ];
712
713         if (*p == '"')
714                 return p;
715         for (cp = p; *cp; cp++)
716                 for (i = 0; special[i].lx_chr; i++)
717                         if (*cp == special[i].lx_chr) {
718                                 sprintf(buffer, "\"%s\"", p);
719                                 return buffer;
720                         }
721
722         return p;
723 }