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