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 }