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    }