]> Pileus Git - ~andy/fetchmail/blob - rfc822valid.c
Complete Dominik's name.
[~andy/fetchmail] / rfc822valid.c
1 /* rfc822valid.c -- validators for RFC-822 syntax
2  * (C) Copyright 2007 Matthias Andree <matthias.andree@gmx.de>
3  * GNU General Public License v2 */
4
5 /* This works only on ASCII-based computers. */
6
7 #include "fetchmail.h"
8 #include <string.h>
9
10 /* CHAR except specials, SPACE, CTLs */
11 static const char *atomchar = "!#$%&'*+-/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~";
12
13 static int quotedpair(unsigned char const **x) {
14     if (**x != '\\') return 0;
15     ++ *x;
16     if ((int)* *x > 127 || * *x == '\0')
17         /* XXX FIXME: 0 is a legal CHAR, so the == '\0' is sort of bogus
18          * above, but fetchmail does not currently deal with NUL inputs
19          * so we don't need to make the distinction between
20          * end-of-string and quoted NUL. */
21         return 0;
22     ++ *x;
23     return 1;
24 }
25
26
27 static int quotedstring(unsigned char const **x) {
28     if (* *x != '"') return 0;
29     ++ *x;
30     for(;;) {
31         switch (* *x) {
32             case '"':
33                 ++ *x;
34                 return 1;
35             case '\\':
36                 if (quotedpair(x) == 0) return 0;
37                 continue;
38             case '\r':
39             case '\0':
40                 return 0;
41         }
42         if ((int)* *x >= 128) {
43             return 0;
44         }
45         ++ *x;
46     }
47 }
48
49 static int atom(unsigned char const **x) {
50     /* atom */
51     if (strchr(atomchar, (char)**x)) {
52         *x += strspn((const char *)*x, atomchar);
53         return 1;
54     }
55     /* invalid character */
56     return 0;
57 }
58
59 static int word(unsigned char const **x) {
60     if (**x == '"')
61         return quotedstring(x);
62     return atom(x);
63 }
64
65 static int domain_literal(unsigned char const **x) {
66     if (**x != '[') return 0;
67     ++ *x;
68     for(;;) {
69         switch (* *x) {
70             case '\0':
71             case '\r':
72             case '[':
73                 return 0;
74             case ']':
75                 ++ *x;
76                 return 1;
77             case '\\':
78                 if (quotedpair(x) == 0) return 0;
79                 continue;
80         }
81         if ((int)* *x > 127) return 0;
82         ++ *x;
83     }
84 }
85
86 static int subdomain(unsigned char const **x) {
87     if (* *x == '[') return domain_literal(x);
88     return atom(x);
89 }
90
91 int rfc822_valid_msgid(const unsigned char *x) {
92     /* expect "<" */
93     if (*x != '<') return 0;
94     ++ x;
95
96     /* expect local-part = word *("." word)
97      * where
98      * word = atom/quoted-string
99      * atom = 1*ATOMCHAR
100      * quoted-string = <"> *(qtext/quoted-pair) <">
101      * qtext = CHAR except ", \, CR
102      * quoted-pair = "\" CHAR
103      */
104     for(;;) {
105         if (word(&x) == 0) return 0;
106         if (*x == '.') { ++x; continue; }
107         if (*x == '@') break;
108         return 0;
109     }
110
111     /* expect "@" */
112     if (*x != '@') return 0;
113     ++ x;
114
115     /* expect domain = sub-domain *("." sub-domain)
116      * sub-domain = domain-ref/domain-literal
117      * domain-ref = atom
118      * domain-literal = "[" *(dtext/quoted-pair) "]" */
119     for(;;) {
120         if (subdomain(&x) == 0) return 0;
121         if (*x == '.') { ++x; continue; }
122         if (*x == '>') break;
123         return 0;
124     }
125
126     if (*x != '>') return 0;
127     return 1;
128 }
129
130 #ifdef TEST
131 #include <stdio.h>
132
133 int main(int argc, char **argv) {
134     int i;
135     for (i = 1; i < argc; i++) {
136         printf("%s: %s\n", argv[i], rfc822_valid_msgid((unsigned char *)argv[i]) ? "OK" : "INVALID");
137     }
138     return 0;
139 }
140 #endif