001 /*
002 * Copyright 2010 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package com.hs.mail.imap.parser;
017
018 import java.io.StringReader;
019 import java.util.LinkedList;
020
021 /**
022 * Parses IMAP command into a list of tokens.
023 *
024 * @author Won Chul Doh
025 * @since 12 Jan, 2010
026 *
027 */
028 public class CommandParser extends AbstractImapCommandParser {
029
030 public CommandParser(StringReader in) {
031 super(in);
032 }
033
034 /******************************************
035 * THE COMMAND GRAMMAR STARTS HERE *
036 ******************************************/
037
038 private boolean append() {
039 if (!kw("APPEND") || !sp() || !mailbox() || !sp())
040 return false;
041 flag_list();
042 sp();
043 date_time();
044 sp();
045 return literal();
046 }
047
048 private boolean authenticate() {
049 return kw("AUTHENTICATE") && sp() && auth_type();
050 }
051
052 private boolean auth_type() {
053 return atom();
054 }
055
056 public LinkedList<Token> command() {
057 if (tag()
058 && sp()
059 && (command_any() || command_auth() || command_nonauth() || command_select())
060 && crlf())
061 return tokens;
062 else
063 throw new ParseException(tokens, "Syntax error in command");
064 }
065
066 private boolean command_any() {
067 return kw("CAPABILITY") || kw("LOGOUT") || kw("NOOP");
068 }
069
070 private boolean command_auth() {
071 return append() || create() || deleteacl() || delete() || examine()
072 || getacl() || getquotaroot() || getquota() || listrights()
073 || list() || lsub() || myrights() || namespace() || rename()
074 || select() || setacl() || setquota() || status()
075 || subscribe() || unsubscribe();
076 }
077
078 private boolean command_nonauth() {
079 return login() || authenticate() || kw("STARTTLS");
080 }
081
082 private boolean command_select() {
083 return kw("CHECK") || kw("CLOSE") || kw("EXPUNGE") || copy() || fetch()
084 || search() || sort() || store() || thread() || uid();
085 }
086
087 private boolean copy() {
088 return kw("COPY") && sp() && sequence_set() && sp() && mailbox();
089 }
090
091 private boolean create() {
092 return kw("CREATE") && sp() && mailbox();
093 }
094
095 private boolean delete() {
096 return kw("DELETE") && sp() && mailbox();
097 }
098
099 private boolean deleteacl() {
100 return kw("DELETEACL") && sp() && mailbox() && sp() && userid();
101 }
102
103 private boolean examine() {
104 return kw("EXAMINE") && sp() && mailbox();
105 }
106
107 private boolean fetch() {
108 if (!kw("FETCH") || !sp() || !sequence_set() || !sp())
109 return false;
110 if (kw("ALL") || kw("FULL") || kw("FAST") || fetch_att())
111 return true;
112 if (!lparen())
113 return false;
114 while (!rparen()) {
115 sp();
116 if (!fetch_att())
117 return false;
118 }
119 return true;
120 }
121
122 private boolean fetch_att() {
123 if (kw("ENVELOPE") || kw("FLAGS") || kw("INTERNALDATE")
124 || kw("RFC822.HEADER") || kw("RFC822.SIZE")
125 || kw("RFC822.TEXT") || kw("RFC822") || kw("BODYSTRUCTURE")
126 || kw("UID")) {
127 return true;
128 }
129 if (kw("BODY.PEEK") || kw("BODY")) {
130 if (!section()) {
131 return false;
132 } else {
133 fetch_att_part();
134 return true;
135 }
136 } else {
137 return false;
138 }
139 }
140
141 private boolean fetch_att_part() {
142 return kw("<") && number() && kw(".") && nz_number() && kw(">");
143 }
144
145 private boolean flag() {
146 return kw("\\Answered") || kw("\\Flagged") || kw("\\Deleted")
147 || kw("\\Seen") || kw("\\Draft") || flag_keyword()
148 || flag_extension();
149 }
150
151 private boolean flag_extension() {
152 return kw("\\") && atom();
153 }
154
155 private boolean flag_keyword() {
156 return atom();
157 }
158
159 private boolean flag_list() {
160 if (!lparen())
161 return false;
162 while (!rparen()) {
163 sp();
164 if (!flag())
165 return false;
166 }
167 return true;
168 }
169
170 private boolean getacl() {
171 return kw("GETACL") && sp() && mailbox();
172 }
173
174 private boolean getquota() {
175 return kw("GETQUOTA") && sp() && astring();
176 }
177
178 private boolean getquotaroot() {
179 return kw("GETQUOTAROOT") && sp() && mailbox();
180 }
181
182 private boolean header_fld_name() {
183 return kw("FROM") || kw("TO") || kw("CC") || kw("BCC") || kw("SUBJECT")
184 || astring();
185 }
186
187 private boolean header_list() {
188 if (!lparen())
189 return false;
190 while (!rparen()) {
191 sp();
192 if (!header_fld_name())
193 return false;
194 }
195 return true;
196 }
197
198 private boolean list() {
199 return kw("LIST") && sp() && mailbox() && sp() && list_mailbox();
200 }
201
202 private boolean list_mailbox() {
203 if (list_char(read())) {
204 while (list_char(read()))
205 ;
206 unread();
207 newToken(Token.Type.LIST_MAILBOX);
208 return true;
209 } else {
210 unread();
211 return string();
212 }
213 }
214
215 private boolean listrights() {
216 return kw("LISTRIGHTS") && sp() && mailbox() && sp() && userid();
217 }
218
219 private boolean login() {
220 return kw("LOGIN") && sp() && userid() && sp() && password();
221 }
222
223 private boolean lsub() {
224 return kw("LSUB") && sp() && mailbox() && sp() && list_mailbox();
225 }
226
227 private boolean mailbox() {
228 return kw("INBOX") || astring();
229 }
230
231 private boolean myrights() {
232 return kw("MYRIGHTS") && sp() && mailbox();
233 }
234
235 private boolean namespace() {
236 return kw("NAMESPACE");
237 }
238
239 private boolean password() {
240 return astring();
241 }
242
243 private boolean rename() {
244 return kw("RENAME") && sp() && mailbox() && sp() && mailbox();
245 }
246
247 private boolean search() {
248 if (!kw("SEARCH") || !sp())
249 return false;
250 if (kw("CHARSET") && (!sp() || !astring() || !sp()))
251 return false;
252 do
253 if (!search_key())
254 return false;
255 while (sp());
256 return true;
257 }
258
259 private boolean search_key() {
260 if (kw("ALL") || kw("ANSWERED") || kw("BCC") && sp() && astring()
261 || kw("BEFORE") && sp() && date() || kw("BODY") && sp()
262 && astring() || kw("CC") && sp() && astring() || kw("DELETED")
263 || kw("FLAGGED") || kw("FROM") && sp() && astring()
264 || kw("KEYWORD") && sp() && flag_keyword() || kw("NEW")
265 || kw("OLD") || kw("ON") && sp() && date() || kw("RECENT")
266 || kw("SEEN") || kw("SINCE") && sp() && date() || kw("SUBJECT")
267 && sp() && astring() || kw("TEXT") && sp() && astring()
268 || kw("TO") && sp() && astring() || kw("UNANSWERED")
269 || kw("UNDELETED") || kw("UNFLAGGED") || kw("UNKEYWORD")
270 && sp() && flag_keyword() || kw("UNSEEN") || kw("DRAFT")
271 || kw("HEADER") && sp() && header_fld_name() && sp()
272 && astring() || kw("LARGER") && sp() && number() || kw("NOT")
273 && sp() && search_key() || kw("OR") && sp() && search_key()
274 && sp() && search_key() || kw("SENTBEFORE") && sp() && date()
275 || kw("SENTON") && sp() && date() || kw("SENTSINCE") && sp()
276 && date() || kw("SMALLER") && sp() && number() || kw("UID")
277 && sp() && sequence_set() || kw("UNDRAFT") || sequence_set())
278 return true;
279 if (!lparen())
280 return false;
281 for (; !rparen(); sp())
282 if (!search_key())
283 return false;
284
285 return true;
286 }
287
288 private boolean section() {
289 if (!kw("["))
290 return false;
291 section_spec();
292 return kw("]");
293 }
294
295 private boolean section_msgtext() {
296 return (kw("HEADER.FIELDS.NOT") || kw("HEADER.FIELDS")) && sp()
297 && header_list() || kw("HEADER") || kw("TEXT");
298 }
299
300 private boolean section_part() {
301 if (!number())
302 return false;
303 for (; kw("."); number())
304 ;
305 return true;
306 }
307
308 private boolean section_spec() {
309 if (section_msgtext())
310 return true;
311 if (section_part()) {
312 section_text();
313 return true;
314 } else
315 return false;
316 }
317
318 private boolean section_text() {
319 return section_msgtext() || kw("MIME");
320 }
321
322 private boolean select() {
323 return kw("SELECT") && sp() && mailbox();
324 }
325
326 private boolean setacl() {
327 return kw("SETACL") && sp() && mailbox() && sp() && userid() && sp()
328 && astring();
329 }
330
331 private boolean setquota() {
332 return kw("SETQUOTA") && sp() && astring() && sp() && setquota_list();
333 }
334
335 private boolean setquota_list() {
336 if (!lparen())
337 return false;
338 while (!rparen()) {
339 if (!setquota_resource())
340 return false;
341 }
342 return true;
343 }
344
345 private boolean setquota_resource() {
346 return atom() && sp() && number();
347 }
348
349 private boolean sort() {
350 if (!kw("SORT") || !sp() || !sort_criteria() || !kw("CHARSET") || !sp())
351 return false;
352 do
353 if (!search_key())
354 return false;
355 while (sp());
356 return true;
357 }
358
359 private boolean sort_criteria() {
360 if (!lparen())
361 return false;
362 for (; !rparen(); sp())
363 if (!sort_criterion())
364 return false;
365 return true;
366 }
367
368 private boolean sort_criterion() {
369 if (kw("REVERSE") && sp())
370 return sort_key();
371 else
372 return sort_key();
373 }
374
375 private boolean sort_key() {
376 return (kw("ARRIVAL") || kw("CC") || kw("DATE") || kw("FROM")
377 || kw("SIZE") || kw("SUBJECT") || kw("TO"));
378 }
379
380 private boolean status() {
381 if (!kw("STATUS") || !sp() || !mailbox() || !sp())
382 return false;
383 if (!lparen())
384 return false;
385 while (!rparen()) {
386 sp();
387 if (!status_att())
388 return false;
389 }
390 return true;
391 }
392
393 private boolean status_att() {
394 return kw("MESSAGES") || kw("RECENT") || kw("UIDNEXT")
395 || kw("UIDVALIDITY") || kw("UNSEEN");
396 }
397
398 private boolean store() {
399 return kw("STORE") && sp() && sequence_set() && sp()
400 && store_att_flags();
401 }
402
403 private boolean store_att_flags() {
404 if (!kw("+FLAGS.SILENT") && !kw("-FLAGS.SILENT") && !kw("+FLAGS")
405 && !kw("-FLAGS") && !kw("FLAGS.SILENT") && !kw("FLAGS")
406 || !sp())
407 return false;
408 if (flag_list())
409 return true;
410 do
411 if (!flag())
412 return false;
413 while (sp());
414 return true;
415 }
416
417 private boolean subscribe() {
418 return kw("SUBSCRIBE") && sp() && mailbox();
419 }
420
421 private boolean tag() {
422 if (tag_char(read())) {
423 while (tag_char(read()))
424 ;
425 unread();
426 newToken(Token.Type.TAG);
427 return true;
428 } else {
429 unread();
430 return false;
431 }
432 }
433
434 private boolean thread() {
435 if (!kw("THREAD") || !sp() || !thread_alg() || !sp())
436 return false;
437 do
438 if (!search_key())
439 return false;
440 while (sp());
441 return true;
442 }
443
444 private boolean thread_alg() {
445 return kw("ORDEREDSUBJECT") || kw("REFERENCES") || atom();
446 }
447
448 private boolean uid() {
449 return kw("UID") && sp()
450 && (copy() || fetch() || search() || sort() || store() || thread());
451 }
452
453 private boolean unsubscribe() {
454 return kw("UNSUBSCRIBE") && sp() && mailbox();
455 }
456
457 private boolean userid() {
458 return astring();
459 }
460
461 /******************************************
462 * THE COMMAND GRAMMAR ENDS HERE *
463 ******************************************/
464
465 }