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