1
2
3
4
5
6
7
8
9
10
11
12
13 package org.abstracthorizon.mercury.smtp;
14
15 import java.io.EOFException;
16 import java.io.IOException;
17 import java.io.InputStream;
18
19 import org.abstracthorizon.mercury.smtp.exception.ParserException;
20 import org.abstracthorizon.mercury.smtp.util.Path;
21
22
23
24
25
26
27 public class SMTPScanner {
28
29
30 protected InputStream in;
31
32
33 protected char[] buffer;
34
35
36 protected boolean literal;
37
38
39 protected int ptr = -1;
40
41
42 protected boolean eol = false;
43
44
45
46
47
48 public SMTPScanner(InputStream in) {
49 this.in = in;
50 in.mark(128);
51 }
52
53
54
55
56 public void resetEOL() {
57 eol = false;
58 }
59
60 protected boolean atom_char(char c) {
61 if ((c >= 31) && (c != '(') && (c != ')') && (c != '{') && (c != ' ') && (c != '%') && (c != '*') && (c != '"') && (c != ']') && (c != '\\') && (c != '.') && (c != '>') && (c != '@')) { return true; }
62 return false;
63 }
64
65 protected boolean astring_char(char c) {
66 if (atom_char(c) || (c == ']')) { return true; }
67 return false;
68 }
69
70 protected boolean text_char(char c) {
71 if ((c != '\r') && (c != '\n')) { return true; }
72 return false;
73 }
74
75 protected boolean digit_nz(char c) {
76 return (c >= '1') && (c <= '9');
77 }
78
79 protected boolean digit(char c) {
80 return (c >= '0') && (c <= '9');
81 }
82
83 protected boolean list_char(char c) {
84 return atom_char(c) || (c == '%') || (c == '*') || (c == ']');
85 }
86
87 protected boolean alfa(char c) {
88 return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'));
89 }
90
91 protected boolean alfa_digit(char c) {
92 return alfa(c) || digit(c);
93 }
94
95 protected boolean ldh(char c) {
96 return ((c == '-') || alfa(c) || digit(c));
97 }
98
99 public char readChar() throws IOException {
100 int i = in.read();
101 if (i >= 0) {
102 return (char)i;
103 }
104 throw new EOFException();
105 }
106
107 public void skip_line() throws IOException {
108 if (eol) {
109 eol = false;
110 return;
111 }
112 char c = readChar();
113 while (true) {
114 while (c != '\r') {
115 c = readChar();
116 }
117 c = readChar();
118 if (c == '\n') {
119 eol = true;
120 return;
121 }
122 }
123 }
124
125 public void check_eol() throws IOException, ParserException {
126 char c = readChar();
127 if (c != '\r') { throw new ParserException("<CR>"); }
128 c = readChar();
129 if (c != '\n') { throw new ParserException("<LF>"); }
130 eol = true;
131 }
132
133 public boolean is_char(char r) throws IOException {
134 in.mark(1);
135 char c = readChar();
136 if (c == r) { return true; }
137 in.reset();
138 return false;
139 }
140
141 public boolean peek_char(char r) throws IOException {
142 in.mark(1);
143 char c = readChar();
144 in.reset();
145 if (c == r) { return true; }
146 return false;
147 }
148
149 public boolean number(Number num) throws IOException {
150 in.mark(10);
151 int len = 0;
152 num.number = 0;
153 char c = readChar();
154 if ((c < '0') && (c > '9')) {
155 in.reset();
156 return false;
157 }
158 num.number = num.number * 10 + (c - '0');
159 len = len + 1;
160 c = readChar();
161 while ((c >= '0') && (c <= '9')) {
162 num.number = num.number * 10 + (c - '0');
163 len = len + 1;
164 c = readChar();
165 }
166 in.reset();
167 in.skip(len);
168 return true;
169 }
170
171 public boolean nz_number(Number num) throws IOException {
172 in.mark(10);
173 int len = 0;
174 num.number = 0;
175 char c = readChar();
176 if ((c < '1') || (c > '9')) {
177 in.reset();
178 return false;
179 }
180 num.number = num.number * 10 + (c - '0');
181 len = len + 1;
182 c = readChar();
183 while ((c >= '0') && (c <= '9')) {
184 num.number = num.number * 10 + (c - '0');
185 len = len + 1;
186 c = readChar();
187 }
188 in.reset();
189 in.skip(len);
190 return true;
191 }
192
193 public boolean keyword(String keyword) throws IOException {
194 in.mark(keyword.length() + 1);
195 ptr = 0;
196 while (ptr < keyword.length()) {
197 char c = readChar();
198 if (Character.toUpperCase(c) != Character.toUpperCase(keyword.charAt(ptr))) {
199 in.reset();
200
201 return false;
202 }
203 ptr = ptr + 1;
204 }
205 return true;
206 }
207
208 public boolean quoted(StringBuffer quoted) throws IOException, ParserException {
209 in.mark(512);
210 char c = readChar();
211 if (c != '"') {
212 in.reset();
213 return false;
214 }
215 c = readChar();
216 while (text_char(c) && (c != '"')) {
217 if (c == '\\') {
218 c = readChar();
219 if ((c == '\\') || (c == '"')) {
220 quoted.append(c);
221 } else {
222 in.reset();
223 throw new ParserException("'" + c + "'");
224 }
225 } else {
226 quoted.append(c);
227 }
228 c = readChar();
229 }
230 if (c != '"') {
231 in.reset();
232 throw new ParserException("'\"'");
233 }
234 return true;
235 }
236
237 public boolean atom(StringBuffer buffer) throws IOException, ParserException {
238 in.mark(128);
239 char c = readChar();
240 if (!atom_char(c)) {
241 in.reset();
242 return false;
243 }
244 buffer.append(c);
245 c = readChar();
246 while (atom_char(c)) {
247 buffer.append(c);
248 c = readChar();
249 }
250 in.reset();
251 in.skip(buffer.length());
252 return true;
253 }
254
255 public boolean ldhStr(StringBuffer buffer) throws IOException, ParserException {
256 in.mark(128);
257 char c = readChar();
258 char l = c;
259 if (!alfa_digit(c)) {
260 in.reset();
261 return false;
262 }
263 buffer.append(c);
264 l = c;
265 c = readChar();
266 while (ldh(c)) {
267 buffer.append(c);
268 l = c;
269 c = readChar();
270 }
271 if (!alfa_digit(l)) { throw new ParserException("alfa-digit"); }
272 in.reset();
273 in.skip(buffer.length());
274 return true;
275 }
276
277 public boolean path(Path path) throws IOException, ParserException {
278 char c = readChar();
279 if (c != '<') {
280 in.reset();
281 return false;
282 }
283 StringBuffer domain = new StringBuffer();
284 if (atDomain(domain)) {
285 path.addReturnPath(domain.toString());
286 domain.delete(0, domain.length());
287 c = readChar();
288 while (c == ',') {
289 if (!atDomain(domain)) {
290 in.reset();
291 throw new ParserException("atDomain");
292 }
293 path.addReturnPath(domain.toString());
294 domain.delete(0, domain.length());
295 c = readChar();
296 }
297 if (c != ':') {
298 in.reset();
299 throw new ParserException(":");
300 }
301 }
302 StringBuffer local = new StringBuffer();
303 if (!localPart(local)) {
304 in.reset();
305 throw new ParserException("local-part");
306 }
307 path.setMailbox(local.toString());
308 if (!atDomain(domain)) {
309 in.reset();
310 throw new ParserException("atDomain");
311 }
312 path.setDomain(domain.toString());
313 if (!is_char('>')) { throw new ParserException(">"); }
314 return true;
315 }
316
317 public boolean atDomain(StringBuffer domain) throws IOException, ParserException {
318 in.mark(1);
319 char c = readChar();
320 if (c != '@') {
321 in.reset();
322 return false;
323 }
324 return domain(domain);
325 }
326
327 public boolean domain(StringBuffer domain) throws IOException, ParserException {
328 if (!ldhStr(domain)) { return false; }
329 in.mark(1);
330 char c = readChar();
331 while (c == '.') {
332 domain.append(c);
333 StringBuffer sd = new StringBuffer();
334 if (!ldhStr(sd)) { throw new ParserException("ldhStr"); }
335 domain.append(sd);
336 sd.delete(0, sd.length());
337 in.mark(1);
338 c = readChar();
339 }
340 in.reset();
341 return true;
342 }
343
344 public boolean localPart(StringBuffer buffer) throws IOException, ParserException {
345 if (quoted(buffer)) { return true; }
346 if (!atom(buffer)) { return false; }
347 in.mark(1);
348 char c = readChar();
349 while (c == '.') {
350 buffer.append(c);
351 StringBuffer p = new StringBuffer();
352 if (!atom(p)) { throw new ParserException("atom"); }
353 buffer.append(p);
354 in.mark(1);
355 c = readChar();
356 }
357 in.reset();
358 return true;
359 }
360
361 public static class Number {
362
363 public int number = 0;
364 }
365
366 }