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 * https://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 * Constructs a new instance. 197 */ 198 public IMAPClient() { 199 // empty 200 } 201 202 /** 203 * Send an APPEND command to the server. 204 * 205 * @param mailboxName The mailbox name. 206 * @return {@code true} if the command was successful,{@code false} if not. 207 * @throws IOException If a network I/O error occurs. 208 * @deprecated (3.4) Does not work; the message body is not optional. Use {@link #append(String, String, String, String)} instead. 209 */ 210 @Deprecated 211 public boolean append(final String mailboxName) throws IOException { 212 return append(mailboxName, null, null); 213 } 214 215 // commands available in the authenticated state 216 217 /** 218 * Send an APPEND command to the server. 219 * 220 * @param mailboxName The mailbox name. 221 * @param flags The flag parenthesized list (optional). 222 * @param datetime The date/time string (optional). 223 * @return {@code true} if the command was successful,{@code false} if not. 224 * @throws IOException If a network I/O error occurs. 225 * @deprecated (3.4) Does not work; the message body is not optional. Use {@link #append(String, String, String, String)} instead. 226 */ 227 @Deprecated 228 public boolean append(final String mailboxName, final String flags, final String datetime) throws IOException { 229 final StringBuilder args = new StringBuilder().append(mailboxName); 230 if (flags != null) { 231 args.append(" ").append(flags); 232 } 233 if (datetime != null) { 234 if (datetime.charAt(0) == '{') { 235 args.append(" ").append(datetime); 236 } else { 237 args.append(" {").append(datetime).append("}"); 238 } 239 } 240 return doCommand(IMAPCommand.APPEND, args.toString()); 241 } 242 243 /** 244 * Send an APPEND command to the server. 245 * 246 * @param mailboxName The mailbox name. 247 * @param flags The flag parenthesized list (optional). 248 * @param datetime The date/time string (optional). 249 * @param message The message to append. 250 * @return {@code true} if the command was successful,{@code false} if not. 251 * @throws IOException If a network I/O error occurs. 252 * @since 3.4 253 */ 254 public boolean append(final String mailboxName, final String flags, final String datetime, final String message) throws IOException { 255 final StringBuilder args = new StringBuilder(quoteMailboxName(mailboxName)); 256 if (flags != null) { 257 args.append(" ").append(flags); 258 } 259 if (datetime != null) { 260 args.append(" "); 261 if (datetime.charAt(0) == DQUOTE) { 262 args.append(datetime); 263 } else { 264 args.append(DQUOTE).append(datetime).append(DQUOTE); 265 } 266 } 267 args.append(" "); 268 // String literal (probably not used much - if at all) 269 if (message.startsWith(DQUOTE_S) && message.endsWith(DQUOTE_S)) { 270 args.append(message); 271 return doCommand(IMAPCommand.APPEND, args.toString()); 272 } 273 args.append('{').append(message.getBytes(__DEFAULT_ENCODING).length).append('}'); // length of message 274 final int status = sendCommand(IMAPCommand.APPEND, args.toString()); 275 return IMAPReply.isContinuation(status) // expecting continuation response 276 && IMAPReply.isSuccess(sendData(message)); // if so, send the data 277 } 278 279 /** 280 * Send a CAPABILITY command to the server. 281 * 282 * @return {@code true} if the command was successful,{@code false} if not. 283 * @throws IOException If a network I/O error occurs 284 */ 285 public boolean capability() throws IOException { 286 return doCommand(IMAPCommand.CAPABILITY); 287 } 288 289 /** 290 * Send a CHECK command to the server. 291 * 292 * @return {@code true} if the command was successful,{@code false} if not. 293 * @throws IOException If a network I/O error occurs. 294 */ 295 public boolean check() throws IOException { 296 return doCommand(IMAPCommand.CHECK); 297 } 298 299 /** 300 * Send a CLOSE command to the server. 301 * 302 * @return {@code true} if the command was successful,{@code false} if not. 303 * @throws IOException If a network I/O error occurs. 304 */ 305 public boolean close() throws IOException { 306 return doCommand(IMAPCommand.CLOSE); 307 } 308 309 /** 310 * Send a COPY command to the server. 311 * 312 * @param sequenceSet The sequence set to fetch. 313 * @param mailboxName The mailbox name. 314 * @return {@code true} if the command was successful,{@code false} if not. 315 * @throws IOException If a network I/O error occurs. 316 */ 317 public boolean copy(final String sequenceSet, final String mailboxName) throws IOException { 318 return doCommand(IMAPCommand.COPY, sequenceSet + " " + quoteMailboxName(mailboxName)); 319 } 320 321 /** 322 * Send a CREATE command to the server. 323 * 324 * @param mailboxName The mailbox name to create. 325 * @return {@code true} if the command was successful,{@code false} if not. 326 * @throws IOException If a network I/O error occurs. 327 */ 328 public boolean create(final String mailboxName) throws IOException { 329 return doCommand(IMAPCommand.CREATE, quoteMailboxName(mailboxName)); 330 } 331 332 /** 333 * Send a DELETE command to the server. 334 * 335 * @param mailboxName The mailbox name to delete. 336 * @return {@code true} if the command was successful,{@code false} if not. 337 * @throws IOException If a network I/O error occurs. 338 */ 339 public boolean delete(final String mailboxName) throws IOException { 340 return doCommand(IMAPCommand.DELETE, quoteMailboxName(mailboxName)); 341 } 342 343 /** 344 * Send an EXAMINE command to the server. 345 * 346 * @param mailboxName The mailbox name to examine. 347 * @return {@code true} if the command was successful,{@code false} if not. 348 * @throws IOException If a network I/O error occurs. 349 */ 350 public boolean examine(final String mailboxName) throws IOException { 351 return doCommand(IMAPCommand.EXAMINE, quoteMailboxName(mailboxName)); 352 } 353 354 /** 355 * Send an EXPUNGE command to the server. 356 * 357 * @return {@code true} if the command was successful,{@code false} if not. 358 * @throws IOException If a network I/O error occurs. 359 */ 360 public boolean expunge() throws IOException { 361 return doCommand(IMAPCommand.EXPUNGE); 362 } 363 364 /** 365 * Send a FETCH command to the server. 366 * 367 * @param sequenceSet The sequence set to fetch (e.g. 1:4,6,11,100:*) 368 * @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 369 * enclosed in parentheses, e.g. "(UID FLAGS BODY.PEEK[])" 370 * @return {@code true} if the command was successful,{@code false} if not. 371 * @throws IOException If a network I/O error occurs. 372 * @see #getReplyString() 373 * @see #getReplyStrings() 374 */ 375 public boolean fetch(final String sequenceSet, final String itemNames) throws IOException { 376 return doCommand(IMAPCommand.FETCH, sequenceSet + " " + itemNames); 377 } 378 379 /** 380 * Send a LIST command to the server. Quotes the parameters if necessary. 381 * 382 * @param refName The reference name If empty, indicates that the mailbox name is interpreted as by SELECT. 383 * @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 384 * reference 385 * @return {@code true} if the command was successful,{@code false} if not. 386 * @throws IOException If a network I/O error occurs. 387 */ 388 public boolean list(final String refName, final String mailboxName) throws IOException { 389 return doCommand(IMAPCommand.LIST, quoteMailboxName(refName) + " " + quoteMailboxName(mailboxName)); 390 } 391 392 /** 393 * Login to the IMAP server with the given user and password. You must first connect to the server with 394 * {@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 395 * NOT_AUTH_STATE. After logging in, the client enters the AUTH_STATE. 396 * 397 * @param user The account name being logged in to. 398 * @param password The plain text password of the account. 399 * @return True if the login attempt was successful, false if not. 400 * @throws IOException If a network I/O error occurs in the process of logging in. 401 */ 402 public boolean login(final String user, final String password) throws IOException { 403 if (getState() != IMAP.IMAPState.NOT_AUTH_STATE) { 404 return false; 405 } 406 407 if (!doCommand(IMAPCommand.LOGIN, user + " " + password)) { 408 return false; 409 } 410 411 setState(IMAP.IMAPState.AUTH_STATE); 412 413 return true; 414 } 415 416 // commands available in the selected state 417 418 /** 419 * 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 420 * client is in the not authenticated or authenticated state, it enters the logout on a successful logout. 421 * 422 * @return {@code true} if the command was successful,{@code false} if not. 423 * @throws IOException If a network I/O error occurs. 424 */ 425 public boolean logout() throws IOException { 426 return doCommand(IMAPCommand.LOGOUT); 427 } 428 429 /** 430 * Send an LSUB command to the server. Quotes the parameters if necessary. 431 * 432 * @param refName The reference name. 433 * @param mailboxName The mailbox name. 434 * @return {@code true} if the command was successful,{@code false} if not. 435 * @throws IOException If a network I/O error occurs. 436 */ 437 public boolean lsub(final String refName, final String mailboxName) throws IOException { 438 return doCommand(IMAPCommand.LSUB, quoteMailboxName(refName) + " " + quoteMailboxName(mailboxName)); 439 } 440 441 /** 442 * 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. 443 * 444 * @return {@code true} if the command was successful,{@code false} if not. 445 * @throws IOException If a network I/O error occurs. 446 */ 447 public boolean noop() throws IOException { 448 return doCommand(IMAPCommand.NOOP); 449 } 450 451 /** 452 * Send a RENAME command to the server. 453 * 454 * @param oldMailboxName The existing mailbox name to rename. 455 * @param newMailboxName The new mailbox name. 456 * @return {@code true} if the command was successful,{@code false} if not. 457 * @throws IOException If a network I/O error occurs. 458 */ 459 public boolean rename(final String oldMailboxName, final String newMailboxName) throws IOException { 460 return doCommand(IMAPCommand.RENAME, quoteMailboxName(oldMailboxName) + " " + quoteMailboxName(newMailboxName)); 461 } 462 463 /** 464 * Send a SEARCH command to the server. 465 * 466 * @param criteria The search criteria. 467 * @return {@code true} if the command was successful,{@code false} if not. 468 * @throws IOException If a network I/O error occurs. 469 */ 470 public boolean search(final String criteria) throws IOException { 471 return search(null, criteria); 472 } 473 474 /** 475 * Send a SEARCH command to the server. 476 * 477 * @param charset The charset (optional). 478 * @param criteria The search criteria. 479 * @return {@code true} if the command was successful,{@code false} if not. 480 * @throws IOException If a network I/O error occurs. 481 */ 482 public boolean search(final String charset, final String criteria) throws IOException { 483 final StringBuilder args = new StringBuilder(); 484 if (charset != null) { 485 args.append("CHARSET ").append(charset); 486 } 487 args.append(criteria); 488 return doCommand(IMAPCommand.SEARCH, args.toString()); 489 } 490 491 /** 492 * Send a SELECT command to the server. 493 * 494 * @param mailboxName The mailbox name to select. 495 * @return {@code true} if the command was successful,{@code false} if not. 496 * @throws IOException If a network I/O error occurs. 497 */ 498 public boolean select(final String mailboxName) throws IOException { 499 return doCommand(IMAPCommand.SELECT, quoteMailboxName(mailboxName)); 500 } 501 502 /** 503 * Send a STATUS command to the server. 504 * 505 * @param mailboxName The reference name. 506 * @param itemNames The status data item names. 507 * @return {@code true} if the command was successful,{@code false} if not. 508 * @throws IOException If a network I/O error occurs. 509 */ 510 public boolean status(final String mailboxName, final String[] itemNames) throws IOException { 511 if (itemNames == null || itemNames.length < 1) { 512 throw new IllegalArgumentException("STATUS command requires at least one data item name"); 513 } 514 515 final StringBuilder sb = new StringBuilder(); 516 sb.append(quoteMailboxName(mailboxName)); 517 518 sb.append(" ("); 519 for (int i = 0; i < itemNames.length; i++) { 520 if (i > 0) { 521 sb.append(" "); 522 } 523 sb.append(itemNames[i]); 524 } 525 sb.append(")"); 526 527 return doCommand(IMAPCommand.STATUS, sb.toString()); 528 } 529 530 /** 531 * Send a STORE command to the server. 532 * 533 * @param sequenceSet The sequence set to update (e.g. 2:5) 534 * @param itemNames The item name for the STORE command (i.e. [+|-]FLAGS[.SILENT]) 535 * @param itemValues The item values for the STORE command. (e.g. (\Deleted) ) 536 * @return {@code true} if the command was successful,{@code false} if not. 537 * @throws IOException If a network I/O error occurs. 538 */ 539 public boolean store(final String sequenceSet, final String itemNames, final String itemValues) throws IOException { 540 return doCommand(IMAPCommand.STORE, sequenceSet + " " + itemNames + " " + itemValues); 541 } 542 543 /** 544 * Send a SUBSCRIBE command to the server. 545 * 546 * @param mailboxName The mailbox name to subscribe to. 547 * @return {@code true} if the command was successful,{@code false} if not. 548 * @throws IOException If a network I/O error occurs. 549 */ 550 public boolean subscribe(final String mailboxName) throws IOException { 551 return doCommand(IMAPCommand.SUBSCRIBE, quoteMailboxName(mailboxName)); 552 } 553 554 /** 555 * Send a UID command to the server. 556 * 557 * @param command The command for UID. 558 * @param commandArgs The arguments for the command. 559 * @return {@code true} if the command was successful,{@code false} if not. 560 * @throws IOException If a network I/O error occurs. 561 */ 562 public boolean uid(final String command, final String commandArgs) throws IOException { 563 return doCommand(IMAPCommand.UID, command + " " + commandArgs); 564 } 565 566 /** 567 * Send a UNSUBSCRIBE command to the server. 568 * 569 * @param mailboxName The mailbox name to unsubscribe from. 570 * @return {@code true} if the command was successful,{@code false} if not. 571 * @throws IOException If a network I/O error occurs. 572 */ 573 public boolean unsubscribe(final String mailboxName) throws IOException { 574 return doCommand(IMAPCommand.UNSUBSCRIBE, quoteMailboxName(mailboxName)); 575 } 576 577} 578