Drop register storage class
[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_xmalloc((size_t) (strlen(s) + 2));
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 accomodate 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                 free(pers);
172         if (mbox)
173                 free(mbox);
174         if (host)
175                 free(host);
176         if (path)
177                 free(path);
178         if (grp)
179                 free(grp);
180         if (note)
181                 free(note);
182         pers = mbox = host = path = grp = note = NULL;
183         err[0] = 0;
184
185         if (dp == NULL) {
186                 dp = cp = getcpy(addrs ? addrs : "");
187                 glevel = 0;
188         } else if (cp == NULL) {
189                 free(dp);
190                 dp = NULL;
191                 return NULL;
192         }
193
194         switch (parse_address()) {
195         case DONE:
196                 free(dp);
197                 dp = cp = NULL;
198                 return NULL;
199
200         case OK:
201                 switch (last_lex) {
202                 case LX_COMA:
203                 case LX_END:
204                         break;
205
206                 default:  /* catch trailing comments */
207                         bp = cp;
208                         my_lex(adr);
209                         cp = bp;
210                         break;
211                 }
212                 break;
213
214         default:
215                 break;
216         }
217
218         if (err[0])
219                 for (;;) {
220                         switch (last_lex) {
221                         case LX_COMA:
222                         case LX_END:
223                                 break;
224
225                         default:
226                                 my_lex(adr);
227                                 continue;
228                         }
229                         break;
230                 }
231         while (isspace(*ap))
232                 ap++;
233         if (cp)
234                 sprintf(adr, "%.*s", (int)(cp - ap), ap);
235         else
236                 strcpy(adr, ap);
237         bp = adr + strlen(adr) - 1;
238         if (*bp == ',' || *bp == ';' || *bp == '\n')
239                 *bp = 0;
240
241         adrxp->text = adr;
242         adrxp->pers = pers;
243         adrxp->mbox = mbox;
244         adrxp->host = host;
245         adrxp->path = path;
246         adrxp->grp = grp;
247         adrxp->ingrp = ingrp;
248         adrxp->note = note;
249         adrxp->err = err[0] ? err : NULL;
250
251         return adrxp;
252 }
253
254
255 static int
256 parse_address(void)
257 {
258         char buffer[BUFSIZ];
259
260 again: ;
261         ap = cp;
262         switch (my_lex(buffer)) {
263         case LX_ATOM:
264         case LX_QSTR:
265                 pers = getcpy(buffer);
266                 break;
267
268         case LX_SEMI:
269                 if (glevel-- <= 0) {
270                         strcpy(err, "extraneous semi-colon");
271                         return NOTOK;
272                 }
273         case LX_COMA:
274                 if (note) {
275                         free(note);
276                         note = NULL;
277                 }
278                 goto again;
279
280         case LX_END:
281                 return DONE;
282
283         case LX_LBRK:  /* sigh (2) */
284                 goto get_addr;
285
286         case LX_AT:  /* sigh (3) */
287                 cp = ap;
288                 if (route_addr(buffer) == NOTOK)
289                         return NOTOK;
290                 return OK;  /* why be choosy? */
291
292         default:
293                 sprintf(err, "illegal address construct (%s)", buffer);
294                 return NOTOK;
295         }
296
297         switch (my_lex(buffer)) {
298         case LX_ATOM:
299         case LX_QSTR:
300                 pers = add(buffer, add(" ", pers));
301 more_phrase: ;  /* sigh (1) */
302                 if (phrase(buffer) == NOTOK)
303                         return NOTOK;
304
305                 switch (last_lex) {
306                 case LX_LBRK:
307 get_addr: ;
308                         if (route_addr(buffer) == NOTOK)
309                                 return NOTOK;
310                         if (last_lex == LX_RBRK)
311                                 return OK;
312                         sprintf(err, "missing right-bracket (%s)", buffer);
313                         return NOTOK;
314
315                 case LX_COLN:
316 get_group: ;
317                         if (glevel++ > 0) {
318                                 sprintf(err, "nested groups not allowed (%s)", pers);
319                                 return NOTOK;
320                         }
321                         grp = add(": ", pers);
322                         pers = NULL;
323                         {
324                                 char   *pp = cp;
325
326                                 for (;;)
327                                         switch (my_lex(buffer)) {
328                                         case LX_SEMI:
329                                         case LX_END: /* tsk, tsk */
330                                                 glevel--;
331                                                 return OK;
332
333                                         case LX_COMA:
334                                                 continue;
335
336                                         default:
337                                                 cp = pp;
338                                                 return parse_address();
339                                         }
340                         }
341
342                 case LX_DOT:  /* sigh (1) */
343                         pers = add(".", pers);
344                         goto more_phrase;
345
346                 default:
347                         sprintf(err, "no mailbox in address, only a phrase (%s%s)", pers, buffer);
348                         return NOTOK;
349                 }
350
351         case LX_LBRK:
352                 goto get_addr;
353
354         case LX_COLN:
355                 goto get_group;
356
357         case LX_DOT:
358                 mbox = add(buffer, pers);
359                 pers = NULL;
360                 if (route_addr(buffer) == NOTOK)
361                         return NOTOK;
362                 goto check_end;
363
364         case LX_AT:
365                 ingrp = glevel;
366                 mbox = pers;
367                 pers = NULL;
368                 if (domain(buffer) == NOTOK)
369                         return NOTOK;
370 check_end: ;
371                 switch (last_lex) {
372                 case LX_SEMI:
373                         if (glevel-- <= 0) {
374                                 strcpy(err, "extraneous semi-colon");
375                                 return NOTOK;
376                         }
377                 case LX_COMA:
378                 case LX_END:
379                         return OK;
380
381                 default:
382                         sprintf(err, "junk after local@domain (%s)", buffer);
383                         return NOTOK;
384                 }
385
386         case LX_SEMI:  /* no host */
387         case LX_COMA:
388         case LX_END:
389                 ingrp = glevel;
390                 if (last_lex == LX_SEMI && glevel-- <= 0) {
391                         strcpy(err, "extraneous semi-colon");
392                         return NOTOK;
393                 }
394                 mbox = pers;
395                 pers = NULL;
396                 return OK;
397
398         default:
399                 sprintf(err, "missing mailbox (%s)", buffer);
400                 return NOTOK;
401         }
402 }
403
404
405 static int
406 phrase(char *buffer)
407 {
408         for (;;)
409                 switch (my_lex(buffer)) {
410                 case LX_ATOM:
411                 case LX_QSTR:
412                         pers = add(buffer, add(" ", pers));
413                         continue;
414
415                 default:
416                         return OK;
417                 }
418 }
419
420
421 static int
422 route_addr(char *buffer)
423 {
424         char *pp = cp;
425
426         if (my_lex(buffer) == LX_AT) {
427                 if (route(buffer) == NOTOK)
428                         return NOTOK;
429         }
430         else
431                 cp = pp;
432
433         if (local_part(buffer) == NOTOK)
434                 return NOTOK;
435
436         switch (last_lex) {
437         case LX_AT:
438                 return domain(buffer);
439
440         case LX_SEMI:  /* if in group */
441         case LX_RBRK:  /* no host */
442         case LX_COMA:
443         case LX_END:
444                 return OK;
445
446         default:
447                 sprintf(err, "no at-sign after local-part (%s)", buffer);
448                 return NOTOK;
449         }
450 }
451
452
453 static int
454 local_part(char *buffer)
455 {
456         ingrp = glevel;
457
458         for (;;) {
459                 switch (my_lex(buffer)) {
460                 case LX_ATOM:
461                 case LX_QSTR:
462                         mbox = add(buffer, mbox);
463                         break;
464
465                 default:
466                         sprintf(err, "no mailbox in local-part (%s)", buffer);
467                         return NOTOK;
468                 }
469
470                 switch (my_lex(buffer)) {
471                 case LX_DOT:
472                         mbox = add(buffer, mbox);
473                         continue;
474
475                 default:
476                         return OK;
477                 }
478         }
479 }
480
481
482 static int
483 domain(char *buffer)
484 {
485         for (;;) {
486                 switch (my_lex(buffer)) {
487                 case LX_ATOM:
488                 case LX_DLIT:
489                         host = add(buffer, host);
490                         break;
491
492                 default:
493                         sprintf(err, "no sub-domain in domain-part of address (%s)", buffer);
494                         return NOTOK;
495                 }
496
497                 switch (my_lex(buffer)) {
498                 case LX_DOT:
499                         host = add(buffer, host);
500                         continue;
501
502                 case LX_AT:  /* sigh (0) */
503                         mbox = add(host, add("%", mbox));
504                         free(host);
505                         host = NULL;
506                         continue;
507
508                 default:
509                         return OK;
510                 }
511         }
512 }
513
514
515 static int
516 route(char *buffer)
517 {
518         path = getcpy("@");
519
520         for (;;) {
521                 switch (my_lex(buffer)) {
522                 case LX_ATOM:
523                 case LX_DLIT:
524                         path = add(buffer, path);
525                         break;
526
527                 default:
528                         sprintf(err, "no sub-domain in domain-part of address (%s)", buffer);
529                         return NOTOK;
530                 }
531                 switch (my_lex(buffer)) {
532                 case LX_COMA:
533                         path = add(buffer, path);
534                         for (;;) {
535                                 switch (my_lex(buffer)) {
536                                 case LX_COMA:
537                                         continue;
538
539                                 case LX_AT:
540                                         path = add(buffer, path);
541                                         break;
542
543                                 default:
544                                         sprintf(err, "no at-sign found for next domain in route (%s)",
545                                                         buffer);
546                                 }
547                                 break;
548                         }
549                         continue;
550
551                 case LX_AT:  /* XXX */
552                 case LX_DOT:
553                         path = add(buffer, path);
554                         continue;
555
556                 case LX_COLN:
557                         path = add(buffer, path);
558                         return OK;
559
560                 default:
561                         sprintf(err, "no colon found to terminate route (%s)", buffer);
562                         return NOTOK;
563                 }
564         }
565 }
566
567
568 static int
569 my_lex(char *buffer)
570 {
571         /* buffer should be at least BUFSIZ bytes long */
572         int i;
573         unsigned char c;
574         char *bp;
575
576         /*
577         ** Add C to the buffer bp. After use of this macro *bp is guaranteed
578         ** to be within the buffer.
579         */
580 #define ADDCHR(C)  \
581         do { \
582                 *bp++ = (C); \
583                 if ((bp - buffer) == (BUFSIZ-1)) \
584                         goto my_lex_buffull; \
585         } while (0)
586
587         bp = buffer;
588         *bp = 0;
589         if (!cp)
590                 return (last_lex = LX_END);
591
592         c = *cp++;
593         while (isspace(c))
594                 c = *cp++;
595         if (c == 0) {
596                 cp = NULL;
597                 return (last_lex = LX_END);
598         }
599
600         if (c == '(') {
601                 ADDCHR(c);
602                 for (i = 0;;)
603                         switch (c = *cp++) {
604                         case 0:
605                                 cp = NULL;
606                                 return (last_lex = LX_ERR);
607                         case QUOTE:
608                                 ADDCHR(c);
609                                 if ((c = *cp++) == 0) {
610                                         cp = NULL;
611                                         return (last_lex = LX_ERR);
612                                 }
613                                 ADDCHR(c);
614                                 continue;
615                         case '(':
616                                 i++;
617                         default:
618                                 ADDCHR(c);
619                                 continue;
620                         case ')':
621                                 ADDCHR(c);
622                                 if (--i < 0) {
623                                         *bp = 0;
624                                         note = note ? add(buffer, add(" ", note)) : getcpy(buffer);
625                                         return my_lex(buffer);
626                                 }
627                         }
628         }
629
630         if (c == '"') {
631                 ADDCHR(c);
632                 for (;;)
633                         switch (c = *cp++) {
634                         case 0:
635                                 cp = NULL;
636                                 return (last_lex = LX_ERR);
637                         case QUOTE:
638                                 ADDCHR(c);
639                                 if ((c = *cp++) == 0) {
640                                         cp = NULL;
641                                         return (last_lex = LX_ERR);
642                                 }
643                         default:
644                                 ADDCHR(c);
645                                 continue;
646                         case '"':
647                                 ADDCHR(c);
648                                 *bp = 0;
649                                 return (last_lex = LX_QSTR);
650                         }
651         }
652
653         if (c == '[') {
654                 ADDCHR(c);
655                 for (;;)
656                         switch (c = *cp++) {
657                         case 0:
658                                 cp = NULL;
659                                 return (last_lex = LX_ERR);
660                         case QUOTE:
661                                 ADDCHR(c);
662                                 if ((c = *cp++) == 0) {
663                                         cp = NULL;
664                                         return (last_lex = LX_ERR);
665                                 }
666                         default:
667                                 ADDCHR(c);
668                                 continue;
669                         case ']':
670                                 ADDCHR(c);
671                                 *bp = 0;
672                                 return (last_lex = LX_DLIT);
673                         }
674         }
675
676         ADDCHR(c);
677         *bp = 0;
678         for (i = 0; special[i].lx_chr != 0; i++)
679                 if (c == special[i].lx_chr)
680                         return (last_lex = special[i].lx_val);
681
682         if (iscntrl(c))
683                 return (last_lex = LX_ERR);
684
685         for (;;) {
686                 if ((c = *cp++) == 0)
687                         break;
688                 for (i = 0; special[i].lx_chr != 0; i++)
689                         if (c == special[i].lx_chr)
690                                 goto got_atom;
691                 if (iscntrl(c) || isspace(c))
692                         break;
693                 ADDCHR(c);
694         }
695 got_atom: ;
696         if (c == 0)
697                 cp = NULL;
698         else
699                 cp--;
700         *bp = 0;
701         return LX_ATOM;
702
703  my_lex_buffull:
704         /* Out of buffer space. *bp is the last byte in the buffer */
705         *bp = 0;
706         return (last_lex = LX_ERR);
707 }
708
709
710 char *
711 legal_person(char *p)
712 {
713         int i;
714         char *cp;
715         static char buffer[BUFSIZ];
716
717         if (*p == '"')
718                 return p;
719         for (cp = p; *cp; cp++)
720                 for (i = 0; special[i].lx_chr; i++)
721                         if (*cp == special[i].lx_chr) {
722                                 sprintf(buffer, "\"%s\"", p);
723                                 return buffer;
724                         }
725
726         return p;
727 }