001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.net.imap; 019 020import java.io.IOException; 021 022/** 023 * The IMAPClient class provides the basic functionalities found in an IMAP client. 024 */ 025public class IMAPClient extends IMAP { 026 027 /** 028 * The message data item names for the FETCH command defined in RFC 3501. 029 */ 030 public enum FETCH_ITEM_NAMES { 031 /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE). */ 032 ALL, 033 /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE). */ 034 FAST, 035 /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY). */ 036 FULL, 037 /** Non-extensible form of BODYSTRUCTURE or the text of a particular body section. */ 038 BODY, 039 /** The [MIME-IMB] body structure of the message. */ 040 BODYSTRUCTURE, 041 /** The envelope structure of the message. */ 042 ENVELOPE, 043 /** The flags that are set for this message. */ 044 FLAGS, 045 /** The internal date of the message. */ 046 INTERNALDATE, 047 /** A prefix for RFC-822 item names. */ 048 RFC822, 049 /** The unique identifier for the message. */ 050 UID 051 } 052 053 /** 054 * The search criteria defined in RFC 3501. 055 */ 056 public enum SEARCH_CRITERIA { 057 /** All messages in the mailbox. */ 058 ALL, 059 /** Messages with the \Answered flag set. */ 060 ANSWERED, 061 /** 062 * Messages that contain the specified string in the envelope structure's BCC field. 063 */ 064 BCC, 065 /** 066 * Messages whose internal date (disregarding time and time zone) is earlier than the specified date. 067 */ 068 BEFORE, 069 /** 070 * Messages that contain the specified string in the body of the message. 071 */ 072 BODY, 073 /** 074 * Messages that contain the specified string in the envelope structure's CC field. 075 */ 076 CC, 077 /** Messages with the \Deleted flag set. */ 078 DELETED, 079 /** Messages with the \Draft flag set. */ 080 DRAFT, 081 /** Messages with the \Flagged flag set. */ 082 FLAGGED, 083 /** 084 * Messages that contain the specified string in the envelope structure's FROM field. 085 */ 086 FROM, 087 /** 088 * Messages that have a header with the specified field-name (as defined in [RFC-2822]) and that contains the specified string in the text of the header 089 * (what comes after the colon). If the string to search is zero-length, this matches all messages that have a header line with the specified field-name 090 * regardless of the contents. 091 */ 092 HEADER, 093 /** Messages with the specified keyword flag set. */ 094 KEYWORD, 095 /** 096 * Messages with an [RFC-2822] size larger than the specified number of octets. 097 */ 098 LARGER, 099 /** 100 * Messages that have the \Recent flag set but not the \Seen flag. This is functionally equivalent to "(RECENT UNSEEN)". 101 */ 102 NEW, 103 /** Messages that do not match the specified search key. */ 104 NOT, 105 /** 106 * Messages that do not have the \Recent flag set. This is functionally equivalent to "NOT RECENT" (as opposed to "NOT NEW"). 107 */ 108 OLD, 109 /** 110 * Messages whose internal date (disregarding time and time zone) is within the specified date. 111 */ 112 ON, 113 /** Messages that match either search key. */ 114 OR, 115 /** Messages that have the \Recent flag set. */ 116 RECENT, 117 /** Messages that have the \Seen flag set. */ 118 SEEN, 119 /** 120 * Messages whose [RFC-2822] Date: header (disregarding time and time zone) is earlier than the specified date. 121 */ 122 SENTBEFORE, 123 /** 124 * Messages whose [RFC-2822] Date: header (disregarding time and time zone) is within the specified date. 125 */ 126 SENTON, 127 /** 128 * Messages whose [RFC-2822] Date: header (disregarding time and time zone) is within or later than the specified date. 129 */ 130 SENTSINCE, 131 /** 132 * Messages whose internal date (disregarding time and time zone) is within or later than the specified date. 133 */ 134 SINCE, 135 /** 136 * Messages with an [RFC-2822] size smaller than the specified number of octets. 137 */ 138 SMALLER, 139 /** 140 * Messages that contain the specified string in the envelope structure's SUBJECT field. 141 */ 142 SUBJECT, 143 /** 144 * Messages that contain the specified string in the header or body of the message. 145 */ 146 TEXT, 147 /** 148 * Messages that contain the specified string in the envelope structure's TO field. 149 */ 150 TO, 151 /** 152 * Messages with unique identifiers corresponding to the specified unique identifier set. Sequence set ranges are permitted. 153 */ 154 UID, 155 /** Messages that do not have the \Answered flag set. */ 156 UNANSWERED, 157 /** Messages that do not have the \Deleted flag set. */ 158 UNDELETED, 159 /** Messages that do not have the \Draft flag set. */ 160 UNDRAFT, 161 /** Messages that do not have the \Flagged flag set. */ 162 UNFLAGGED, 163 /** Messages that do not have the specified keyword flag set. */ 164 UNKEYWORD, 165 /** Messages that do not have the \Seen flag set. */ 166 UNSEEN 167 } 168 169 // --------- commands available in all states 170 171 /** 172 * The status data items defined in RFC 3501. 173 */ 174 public enum STATUS_DATA_ITEMS { 175 /** The number of messages in the mailbox. */ 176 MESSAGES, 177 /** The number of messages with the \Recent flag set. */ 178 RECENT, 179 /** The next unique identifier value of the mailbox. */ 180 UIDNEXT, 181 /** The unique identifier validity value of the mailbox. */ 182 UIDVALIDITY, 183 /** The number of messages which do not have the \Seen flag set. */ 184 UNSEEN 185 } 186 187 private static final char DQUOTE = '"'; 188 189 private static final String DQUOTE_S = "\""; 190 191 // --------- commands available in the not-authenticated state 192 // STARTTLS skipped - see IMAPSClient. 193 // AUTHENTICATE skipped - see AuthenticatingIMAPClient. 194 195 /** 196 * Send an APPEND command to the server. 197 * 198 * @param mailboxName The mailbox name. 199 * @return {@code true} if the command was successful,{@code false} if not. 200 * @throws IOException If a network I/O error occurs. 201 * @deprecated (3.4) Does not work; the message body is not optional. Use {@link #append(String, String, String, String)} instead. 202 */ 203 @Deprecated 204 public boolean append(final String mailboxName) throws IOException { 205 return append(mailboxName, null, null); 206 } 207 208 // --------- commands available in the authenticated state 209 210 /** 211 * Send an APPEND command to the server. 212 * 213 * @param mailboxName The mailbox name. 214 * @param flags The flag parenthesized list (optional). 215 * @param datetime The date/time string (optional). 216 * @return {@code true} if the command was successful,{@code false} if not. 217 * @throws IOException If a network I/O error occurs. 218 * @deprecated (3.4) Does not work; the message body is not optional. Use {@link #append(String, String, String, String)} instead. 219 */ 220 @Deprecated 221 public boolean append(final String mailboxName, final String flags, final String datetime) throws IOException { 222 final StringBuilder args = new StringBuilder().append(mailboxName); 223 if (flags != null) { 224 args.append(" ").append(flags); 225 } 226 if (datetime != null) { 227 if (datetime.charAt(0) == '{') { 228 args.append(" ").append(datetime); 229 } else { 230 args.append(" {").append(datetime).append("}"); 231 } 232 } 233 return doCommand(IMAPCommand.APPEND, args.toString()); 234 } 235 236 /** 237 * Send an APPEND command to the server. 238 * 239 * @param mailboxName The mailbox name. 240 * @param flags The flag parenthesized list (optional). 241 * @param datetime The date/time string (optional). 242 * @param message The message to append. 243 * @return {@code true} if the command was successful,{@code false} if not. 244 * @throws IOException If a network I/O error occurs. 245 * @since 3.4 246 */ 247 public boolean append(final String mailboxName, final String flags, final String datetime, final String message) throws IOException { 248 final StringBuilder args = new StringBuilder(quoteMailboxName(mailboxName)); 249 if (flags != null) { 250 args.append(" ").append(flags); 251 } 252 if (datetime != null) { 253 args.append(" "); 254 if (datetime.charAt(0) == DQUOTE) { 255 args.append(datetime); 256 } else { 257 args.append(DQUOTE).append(datetime).append(DQUOTE); 258 } 259 } 260 args.append(" "); 261 // String literal (probably not used much - if at all) 262 if (message.startsWith(DQUOTE_S) && message.endsWith(DQUOTE_S)) { 263 args.append(message); 264 return doCommand(IMAPCommand.APPEND, args.toString()); 265 } 266 args.append('{').append(message.getBytes(IMAP.__DEFAULT_ENCODING).length).append('}'); // length of message 267 final int status = sendCommand(IMAPCommand.APPEND, args.toString()); 268 return IMAPReply.isContinuation(status) // expecting continuation response 269 && IMAPReply.isSuccess(sendData(message)); // if so, send the data 270 } 271 272 /** 273 * Send a CAPABILITY command to the server. 274 * 275 * @return {@code true} if the command was successful,{@code false} if not. 276 * @throws IOException If a network I/O error occurs 277 */ 278 public boolean capability() throws IOException { 279 return doCommand(IMAPCommand.CAPABILITY); 280 } 281 282 /** 283 * Send a CHECK command to the server. 284 * 285 * @return {@code true} if the command was successful,{@code false} if not. 286 * @throws IOException If a network I/O error occurs. 287 */ 288 public boolean check() throws IOException { 289 return doCommand(IMAPCommand.CHECK); 290 } 291 292 /** 293 * Send a CLOSE command to the server. 294 * 295 * @return {@code true} if the command was successful,{@code false} if not. 296 * @throws IOException If a network I/O error occurs. 297 */ 298 public boolean close() throws IOException { 299 return doCommand(IMAPCommand.CLOSE); 300 } 301 302 /** 303 * Send a COPY command to the server. 304 * 305 * @param sequenceSet The sequence set to fetch. 306 * @param mailboxName The mailbox name. 307 * @return {@code true} if the command was successful,{@code false} if not. 308 * @throws IOException If a network I/O error occurs. 309 */ 310 public boolean copy(final String sequenceSet, final String mailboxName) throws IOException { 311 return doCommand(IMAPCommand.COPY, sequenceSet + " " + quoteMailboxName(mailboxName)); 312 } 313 314 /** 315 * Send a CREATE command to the server. 316 * 317 * @param mailboxName The mailbox name to create. 318 * @return {@code true} if the command was successful,{@code false} if not. 319 * @throws IOException If a network I/O error occurs. 320 */ 321 public boolean create(final String mailboxName) throws IOException { 322 return doCommand(IMAPCommand.CREATE, quoteMailboxName(mailboxName)); 323 } 324 325 /** 326 * Send a DELETE command to the server. 327 * 328 * @param mailboxName The mailbox name to delete. 329 * @return {@code true} if the command was successful,{@code false} if not. 330 * @throws IOException If a network I/O error occurs. 331 */ 332 public boolean delete(final String mailboxName) throws IOException { 333 return doCommand(IMAPCommand.DELETE, quoteMailboxName(mailboxName)); 334 } 335 336 /** 337 * Send an EXAMINE command to the server. 338 * 339 * @param mailboxName The mailbox name to examine. 340 * @return {@code true} if the command was successful,{@code false} if not. 341 * @throws IOException If a network I/O error occurs. 342 */ 343 public boolean examine(final String mailboxName) throws IOException { 344 return doCommand(IMAPCommand.EXAMINE, quoteMailboxName(mailboxName)); 345 } 346 347 /** 348 * Send an EXPUNGE command to the server. 349 * 350 * @return {@code true} if the command was successful,{@code false} if not. 351 * @throws IOException If a network I/O error occurs. 352 */ 353 public boolean expunge() throws IOException { 354 return doCommand(IMAPCommand.EXPUNGE); 355 } 356 357 /** 358 * Send a FETCH command to the server. 359 * 360 * @param sequenceSet The sequence set to fetch (e.g. 1:4,6,11,100:*) 361 * @param itemNames The item names for the FETCH command. (e.g. BODY.PEEK[HEADER.FIELDS (SUBJECT)]) If multiple item names are requested, these must be 362 * enclosed in parentheses, e.g. "(UID FLAGS BODY.PEEK[])" 363 * @return {@code true} if the command was successful,{@code false} if not. 364 * @throws IOException If a network I/O error occurs. 365 * @see #getReplyString() 366 * @see #getReplyStrings() 367 */ 368 public boolean fetch(final String sequenceSet, final String itemNames) throws IOException { 369 return doCommand(IMAPCommand.FETCH, sequenceSet + " " + itemNames); 370 } 371 372 /** 373 * Send a LIST command to the server. Quotes the parameters if necessary. 374 * 375 * @param refName The reference name If empty, indicates that the mailbox name is interpreted as by SELECT. 376 * @param mailboxName The mailbox name. If empty, this is a special request to return the hierarchy delimiter and the root name of the name given in the 377 * reference 378 * @return {@code true} if the command was successful,{@code false} if not. 379 * @throws IOException If a network I/O error occurs. 380 */ 381 public boolean list(final String refName, final String mailboxName) throws IOException { 382 return doCommand(IMAPCommand.LIST, quoteMailboxName(refName) + " " + quoteMailboxName(mailboxName)); 383 } 384 385 /** 386 * Login to the IMAP server with the given user and password. You must first connect to the server with 387 * {@link org.apache.commons.net.SocketClient#connect connect } before attempting to log in. A login attempt is only valid if the client is in the 388 * NOT_AUTH_STATE. After logging in, the client enters the AUTH_STATE. 389 * 390 * @param user The account name being logged in to. 391 * @param password The plain text password of the account. 392 * @return True if the login attempt was successful, false if not. 393 * @throws IOException If a network I/O error occurs in the process of logging in. 394 */ 395 public boolean login(final String user, final String password) throws IOException { 396 if (getState() != IMAP.IMAPState.NOT_AUTH_STATE) { 397 return false; 398 } 399 400 if (!doCommand(IMAPCommand.LOGIN, user + " " + password)) { 401 return false; 402 } 403 404 setState(IMAP.IMAPState.AUTH_STATE); 405 406 return true; 407 } 408 409 // --------- commands available in the selected state 410 411 /** 412 * Send a LOGOUT command to the server. To fully disconnect from the server you must call disconnect(). A logout attempt is valid in any state. If the 413 * client is in the not authenticated or authenticated state, it enters the logout on a successful logout. 414 * 415 * @return {@code true} if the command was successful,{@code false} if not. 416 * @throws IOException If a network I/O error occurs. 417 */ 418 public boolean logout() throws IOException { 419 return doCommand(IMAPCommand.LOGOUT); 420 } 421 422 /** 423 * Send an LSUB command to the server. Quotes the parameters if necessary. 424 * 425 * @param refName The reference name. 426 * @param mailboxName The mailbox name. 427 * @return {@code true} if the command was successful,{@code false} if not. 428 * @throws IOException If a network I/O error occurs. 429 */ 430 public boolean lsub(final String refName, final String mailboxName) throws IOException { 431 return doCommand(IMAPCommand.LSUB, quoteMailboxName(refName) + " " + quoteMailboxName(mailboxName)); 432 } 433 434 /** 435 * Send a NOOP command to the server. This is useful for keeping a connection alive since most IMAP servers will time out after 10 minutes of inactivity. 436 * 437 * @return {@code true} if the command was successful,{@code false} if not. 438 * @throws IOException If a network I/O error occurs. 439 */ 440 public boolean noop() throws IOException { 441 return doCommand(IMAPCommand.NOOP); 442 } 443 444 /** 445 * Send a RENAME command to the server. 446 * 447 * @param oldMailboxName The existing mailbox name to rename. 448 * @param newMailboxName The new mailbox name. 449 * @return {@code true} if the command was successful,{@code false} if not. 450 * @throws IOException If a network I/O error occurs. 451 */ 452 public boolean rename(final String oldMailboxName, final String newMailboxName) throws IOException { 453 return doCommand(IMAPCommand.RENAME, quoteMailboxName(oldMailboxName) + " " + quoteMailboxName(newMailboxName)); 454 } 455 456 /** 457 * Send a SEARCH command to the server. 458 * 459 * @param criteria The search criteria. 460 * @return {@code true} if the command was successful,{@code false} if not. 461 * @throws IOException If a network I/O error occurs. 462 */ 463 public boolean search(final String criteria) throws IOException { 464 return search(null, criteria); 465 } 466 467 /** 468 * Send a SEARCH command to the server. 469 * 470 * @param charset The charset (optional). 471 * @param criteria The search criteria. 472 * @return {@code true} if the command was successful,{@code false} if not. 473 * @throws IOException If a network I/O error occurs. 474 */ 475 public boolean search(final String charset, final String criteria) throws IOException { 476 final StringBuilder args = new StringBuilder(); 477 if (charset != null) { 478 args.append("CHARSET ").append(charset); 479 } 480 args.append(criteria); 481 return doCommand(IMAPCommand.SEARCH, args.toString()); 482 } 483 484 /** 485 * Send a SELECT command to the server. 486 * 487 * @param mailboxName The mailbox name to select. 488 * @return {@code true} if the command was successful,{@code false} if not. 489 * @throws IOException If a network I/O error occurs. 490 */ 491 public boolean select(final String mailboxName) throws IOException { 492 return doCommand(IMAPCommand.SELECT, quoteMailboxName(mailboxName)); 493 } 494 495 /** 496 * Send a STATUS command to the server. 497 * 498 * @param mailboxName The reference name. 499 * @param itemNames The status data item names. 500 * @return {@code true} if the command was successful,{@code false} if not. 501 * @throws IOException If a network I/O error occurs. 502 */ 503 public boolean status(final String mailboxName, final String[] itemNames) throws IOException { 504 if (itemNames == null || itemNames.length < 1) { 505 throw new IllegalArgumentException("STATUS command requires at least one data item name"); 506 } 507 508 final StringBuilder sb = new StringBuilder(); 509 sb.append(quoteMailboxName(mailboxName)); 510 511 sb.append(" ("); 512 for (int i = 0; i < itemNames.length; i++) { 513 if (i > 0) { 514 sb.append(" "); 515 } 516 sb.append(itemNames[i]); 517 } 518 sb.append(")"); 519 520 return doCommand(IMAPCommand.STATUS, sb.toString()); 521 } 522 523 /** 524 * Send a STORE command to the server. 525 * 526 * @param sequenceSet The sequence set to update (e.g. 2:5) 527 * @param itemNames The item name for the STORE command (i.e. [+|-]FLAGS[.SILENT]) 528 * @param itemValues The item values for the STORE command. (e.g. (\Deleted) ) 529 * @return {@code true} if the command was successful,{@code false} if not. 530 * @throws IOException If a network I/O error occurs. 531 */ 532 public boolean store(final String sequenceSet, final String itemNames, final String itemValues) throws IOException { 533 return doCommand(IMAPCommand.STORE, sequenceSet + " " + itemNames + " " + itemValues); 534 } 535 536 /** 537 * Send a SUBSCRIBE command to the server. 538 * 539 * @param mailboxName The mailbox name to subscribe to. 540 * @return {@code true} if the command was successful,{@code false} if not. 541 * @throws IOException If a network I/O error occurs. 542 */ 543 public boolean subscribe(final String mailboxName) throws IOException { 544 return doCommand(IMAPCommand.SUBSCRIBE, quoteMailboxName(mailboxName)); 545 } 546 547 /** 548 * Send a UID command to the server. 549 * 550 * @param command The command for UID. 551 * @param commandArgs The arguments for the command. 552 * @return {@code true} if the command was successful,{@code false} if not. 553 * @throws IOException If a network I/O error occurs. 554 */ 555 public boolean uid(final String command, final String commandArgs) throws IOException { 556 return doCommand(IMAPCommand.UID, command + " " + commandArgs); 557 } 558 559 /** 560 * Send a UNSUBSCRIBE command to the server. 561 * 562 * @param mailboxName The mailbox name to unsubscribe from. 563 * @return {@code true} if the command was successful,{@code false} if not. 564 * @throws IOException If a network I/O error occurs. 565 */ 566 public boolean unsubscribe(final String mailboxName) throws IOException { 567 return doCommand(IMAPCommand.UNSUBSCRIBE, quoteMailboxName(mailboxName)); 568 } 569 570} 571