IMAPClient.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.net.imap;

  18. import java.io.IOException;

  19. /**
  20.  * The IMAPClient class provides the basic functionalities found in an IMAP client.
  21.  */
  22. public class IMAPClient extends IMAP {

  23.     /**
  24.      * The message data item names for the FETCH command defined in RFC 3501.
  25.      */
  26.     public enum FETCH_ITEM_NAMES {
  27.         /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE). */
  28.         ALL,
  29.         /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE). */
  30.         FAST,
  31.         /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY). */
  32.         FULL,
  33.         /** Non-extensible form of BODYSTRUCTURE or the text of a particular body section. */
  34.         BODY,
  35.         /** The [MIME-IMB] body structure of the message. */
  36.         BODYSTRUCTURE,
  37.         /** The envelope structure of the message. */
  38.         ENVELOPE,
  39.         /** The flags that are set for this message. */
  40.         FLAGS,
  41.         /** The internal date of the message. */
  42.         INTERNALDATE,
  43.         /** A prefix for RFC-822 item names. */
  44.         RFC822,
  45.         /** The unique identifier for the message. */
  46.         UID
  47.     }

  48.     /**
  49.      * The search criteria defined in RFC 3501.
  50.      */
  51.     public enum SEARCH_CRITERIA {
  52.         /** All messages in the mailbox. */
  53.         ALL,
  54.         /** Messages with the \Answered flag set. */
  55.         ANSWERED,
  56.         /**
  57.          * Messages that contain the specified string in the envelope structure's BCC field.
  58.          */
  59.         BCC,
  60.         /**
  61.          * Messages whose internal date (disregarding time and time zone) is earlier than the specified date.
  62.          */
  63.         BEFORE,
  64.         /**
  65.          * Messages that contain the specified string in the body of the message.
  66.          */
  67.         BODY,
  68.         /**
  69.          * Messages that contain the specified string in the envelope structure's CC field.
  70.          */
  71.         CC,
  72.         /** Messages with the \Deleted flag set. */
  73.         DELETED,
  74.         /** Messages with the \Draft flag set. */
  75.         DRAFT,
  76.         /** Messages with the \Flagged flag set. */
  77.         FLAGGED,
  78.         /**
  79.          * Messages that contain the specified string in the envelope structure's FROM field.
  80.          */
  81.         FROM,
  82.         /**
  83.          * 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
  84.          * (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
  85.          * regardless of the contents.
  86.          */
  87.         HEADER,
  88.         /** Messages with the specified keyword flag set. */
  89.         KEYWORD,
  90.         /**
  91.          * Messages with an [RFC-2822] size larger than the specified number of octets.
  92.          */
  93.         LARGER,
  94.         /**
  95.          * Messages that have the \Recent flag set but not the \Seen flag. This is functionally equivalent to "(RECENT UNSEEN)".
  96.          */
  97.         NEW,
  98.         /** Messages that do not match the specified search key. */
  99.         NOT,
  100.         /**
  101.          * Messages that do not have the \Recent flag set. This is functionally equivalent to "NOT RECENT" (as opposed to "NOT NEW").
  102.          */
  103.         OLD,
  104.         /**
  105.          * Messages whose internal date (disregarding time and time zone) is within the specified date.
  106.          */
  107.         ON,
  108.         /** Messages that match either search key. */
  109.         OR,
  110.         /** Messages that have the \Recent flag set. */
  111.         RECENT,
  112.         /** Messages that have the \Seen flag set. */
  113.         SEEN,
  114.         /**
  115.          * Messages whose [RFC-2822] Date: header (disregarding time and time zone) is earlier than the specified date.
  116.          */
  117.         SENTBEFORE,
  118.         /**
  119.          * Messages whose [RFC-2822] Date: header (disregarding time and time zone) is within the specified date.
  120.          */
  121.         SENTON,
  122.         /**
  123.          * Messages whose [RFC-2822] Date: header (disregarding time and time zone) is within or later than the specified date.
  124.          */
  125.         SENTSINCE,
  126.         /**
  127.          * Messages whose internal date (disregarding time and time zone) is within or later than the specified date.
  128.          */
  129.         SINCE,
  130.         /**
  131.          * Messages with an [RFC-2822] size smaller than the specified number of octets.
  132.          */
  133.         SMALLER,
  134.         /**
  135.          * Messages that contain the specified string in the envelope structure's SUBJECT field.
  136.          */
  137.         SUBJECT,
  138.         /**
  139.          * Messages that contain the specified string in the header or body of the message.
  140.          */
  141.         TEXT,
  142.         /**
  143.          * Messages that contain the specified string in the envelope structure's TO field.
  144.          */
  145.         TO,
  146.         /**
  147.          * Messages with unique identifiers corresponding to the specified unique identifier set. Sequence set ranges are permitted.
  148.          */
  149.         UID,
  150.         /** Messages that do not have the \Answered flag set. */
  151.         UNANSWERED,
  152.         /** Messages that do not have the \Deleted flag set. */
  153.         UNDELETED,
  154.         /** Messages that do not have the \Draft flag set. */
  155.         UNDRAFT,
  156.         /** Messages that do not have the \Flagged flag set. */
  157.         UNFLAGGED,
  158.         /** Messages that do not have the specified keyword flag set. */
  159.         UNKEYWORD,
  160.         /** Messages that do not have the \Seen flag set. */
  161.         UNSEEN
  162.     }

  163.     // commands available in all states

  164.     /**
  165.      * The status data items defined in RFC 3501.
  166.      */
  167.     public enum STATUS_DATA_ITEMS {
  168.         /** The number of messages in the mailbox. */
  169.         MESSAGES,
  170.         /** The number of messages with the \Recent flag set. */
  171.         RECENT,
  172.         /** The next unique identifier value of the mailbox. */
  173.         UIDNEXT,
  174.         /** The unique identifier validity value of the mailbox. */
  175.         UIDVALIDITY,
  176.         /** The number of messages which do not have the \Seen flag set. */
  177.         UNSEEN
  178.     }

  179.     private static final char DQUOTE = '"';

  180.     private static final String DQUOTE_S = "\"";

  181.     // commands available in the not-authenticated state
  182.     // STARTTLS skipped - see IMAPSClient.
  183.     // AUTHENTICATE skipped - see AuthenticatingIMAPClient.

  184.     /**
  185.      * Constructs a new instance.
  186.      */
  187.     public IMAPClient() {
  188.         // empty
  189.     }

  190.     /**
  191.      * Send an APPEND command to the server.
  192.      *
  193.      * @param mailboxName The mailbox name.
  194.      * @return {@code true} if the command was successful,{@code false} if not.
  195.      * @throws IOException If a network I/O error occurs.
  196.      * @deprecated (3.4) Does not work; the message body is not optional. Use {@link #append(String, String, String, String)} instead.
  197.      */
  198.     @Deprecated
  199.     public boolean append(final String mailboxName) throws IOException {
  200.         return append(mailboxName, null, null);
  201.     }

  202.     // commands available in the authenticated state

  203.     /**
  204.      * Send an APPEND command to the server.
  205.      *
  206.      * @param mailboxName The mailbox name.
  207.      * @param flags       The flag parenthesized list (optional).
  208.      * @param datetime    The date/time string (optional).
  209.      * @return {@code true} if the command was successful,{@code false} if not.
  210.      * @throws IOException If a network I/O error occurs.
  211.      * @deprecated (3.4) Does not work; the message body is not optional. Use {@link #append(String, String, String, String)} instead.
  212.      */
  213.     @Deprecated
  214.     public boolean append(final String mailboxName, final String flags, final String datetime) throws IOException {
  215.         final StringBuilder args = new StringBuilder().append(mailboxName);
  216.         if (flags != null) {
  217.             args.append(" ").append(flags);
  218.         }
  219.         if (datetime != null) {
  220.             if (datetime.charAt(0) == '{') {
  221.                 args.append(" ").append(datetime);
  222.             } else {
  223.                 args.append(" {").append(datetime).append("}");
  224.             }
  225.         }
  226.         return doCommand(IMAPCommand.APPEND, args.toString());
  227.     }

  228.     /**
  229.      * Send an APPEND command to the server.
  230.      *
  231.      * @param mailboxName The mailbox name.
  232.      * @param flags       The flag parenthesized list (optional).
  233.      * @param datetime    The date/time string (optional).
  234.      * @param message     The message to append.
  235.      * @return {@code true} if the command was successful,{@code false} if not.
  236.      * @throws IOException If a network I/O error occurs.
  237.      * @since 3.4
  238.      */
  239.     public boolean append(final String mailboxName, final String flags, final String datetime, final String message) throws IOException {
  240.         final StringBuilder args = new StringBuilder(quoteMailboxName(mailboxName));
  241.         if (flags != null) {
  242.             args.append(" ").append(flags);
  243.         }
  244.         if (datetime != null) {
  245.             args.append(" ");
  246.             if (datetime.charAt(0) == DQUOTE) {
  247.                 args.append(datetime);
  248.             } else {
  249.                 args.append(DQUOTE).append(datetime).append(DQUOTE);
  250.             }
  251.         }
  252.         args.append(" ");
  253.         // String literal (probably not used much - if at all)
  254.         if (message.startsWith(DQUOTE_S) && message.endsWith(DQUOTE_S)) {
  255.             args.append(message);
  256.             return doCommand(IMAPCommand.APPEND, args.toString());
  257.         }
  258.         args.append('{').append(message.getBytes(__DEFAULT_ENCODING).length).append('}'); // length of message
  259.         final int status = sendCommand(IMAPCommand.APPEND, args.toString());
  260.         return IMAPReply.isContinuation(status) // expecting continuation response
  261.                 && IMAPReply.isSuccess(sendData(message)); // if so, send the data
  262.     }

  263.     /**
  264.      * Send a CAPABILITY command to the server.
  265.      *
  266.      * @return {@code true} if the command was successful,{@code false} if not.
  267.      * @throws IOException If a network I/O error occurs
  268.      */
  269.     public boolean capability() throws IOException {
  270.         return doCommand(IMAPCommand.CAPABILITY);
  271.     }

  272.     /**
  273.      * Send a CHECK 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 check() throws IOException {
  279.         return doCommand(IMAPCommand.CHECK);
  280.     }

  281.     /**
  282.      * Send a CLOSE command to the server.
  283.      *
  284.      * @return {@code true} if the command was successful,{@code false} if not.
  285.      * @throws IOException If a network I/O error occurs.
  286.      */
  287.     public boolean close() throws IOException {
  288.         return doCommand(IMAPCommand.CLOSE);
  289.     }

  290.     /**
  291.      * Send a COPY command to the server.
  292.      *
  293.      * @param sequenceSet The sequence set to fetch.
  294.      * @param mailboxName The mailbox name.
  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 copy(final String sequenceSet, final String mailboxName) throws IOException {
  299.         return doCommand(IMAPCommand.COPY, sequenceSet + " " + quoteMailboxName(mailboxName));
  300.     }

  301.     /**
  302.      * Send a CREATE command to the server.
  303.      *
  304.      * @param mailboxName The mailbox name to create.
  305.      * @return {@code true} if the command was successful,{@code false} if not.
  306.      * @throws IOException If a network I/O error occurs.
  307.      */
  308.     public boolean create(final String mailboxName) throws IOException {
  309.         return doCommand(IMAPCommand.CREATE, quoteMailboxName(mailboxName));
  310.     }

  311.     /**
  312.      * Send a DELETE command to the server.
  313.      *
  314.      * @param mailboxName The mailbox name to delete.
  315.      * @return {@code true} if the command was successful,{@code false} if not.
  316.      * @throws IOException If a network I/O error occurs.
  317.      */
  318.     public boolean delete(final String mailboxName) throws IOException {
  319.         return doCommand(IMAPCommand.DELETE, quoteMailboxName(mailboxName));
  320.     }

  321.     /**
  322.      * Send an EXAMINE command to the server.
  323.      *
  324.      * @param mailboxName The mailbox name to examine.
  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 examine(final String mailboxName) throws IOException {
  329.         return doCommand(IMAPCommand.EXAMINE, quoteMailboxName(mailboxName));
  330.     }

  331.     /**
  332.      * Send an EXPUNGE command to the server.
  333.      *
  334.      * @return {@code true} if the command was successful,{@code false} if not.
  335.      * @throws IOException If a network I/O error occurs.
  336.      */
  337.     public boolean expunge() throws IOException {
  338.         return doCommand(IMAPCommand.EXPUNGE);
  339.     }

  340.     /**
  341.      * Send a FETCH command to the server.
  342.      *
  343.      * @param sequenceSet The sequence set to fetch (e.g. 1:4,6,11,100:*)
  344.      * @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
  345.      *                    enclosed in parentheses, e.g. "(UID FLAGS BODY.PEEK[])"
  346.      * @return {@code true} if the command was successful,{@code false} if not.
  347.      * @throws IOException If a network I/O error occurs.
  348.      * @see #getReplyString()
  349.      * @see #getReplyStrings()
  350.      */
  351.     public boolean fetch(final String sequenceSet, final String itemNames) throws IOException {
  352.         return doCommand(IMAPCommand.FETCH, sequenceSet + " " + itemNames);
  353.     }

  354.     /**
  355.      * Send a LIST command to the server. Quotes the parameters if necessary.
  356.      *
  357.      * @param refName     The reference name If empty, indicates that the mailbox name is interpreted as by SELECT.
  358.      * @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
  359.      *                    reference
  360.      * @return {@code true} if the command was successful,{@code false} if not.
  361.      * @throws IOException If a network I/O error occurs.
  362.      */
  363.     public boolean list(final String refName, final String mailboxName) throws IOException {
  364.         return doCommand(IMAPCommand.LIST, quoteMailboxName(refName) + " " + quoteMailboxName(mailboxName));
  365.     }

  366.     /**
  367.      * Login to the IMAP server with the given user and password. You must first connect to the server with
  368.      * {@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
  369.      * NOT_AUTH_STATE. After logging in, the client enters the AUTH_STATE.
  370.      *
  371.      * @param user The account name being logged in to.
  372.      * @param password The plain text password of the account.
  373.      * @return True if the login attempt was successful, false if not.
  374.      * @throws IOException If a network I/O error occurs in the process of logging in.
  375.      */
  376.     public boolean login(final String user, final String password) throws IOException {
  377.         if (getState() != IMAP.IMAPState.NOT_AUTH_STATE) {
  378.             return false;
  379.         }

  380.         if (!doCommand(IMAPCommand.LOGIN, user + " " + password)) {
  381.             return false;
  382.         }

  383.         setState(IMAP.IMAPState.AUTH_STATE);

  384.         return true;
  385.     }

  386.     // commands available in the selected state

  387.     /**
  388.      * 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
  389.      * client is in the not authenticated or authenticated state, it enters the logout on a successful logout.
  390.      *
  391.      * @return {@code true} if the command was successful,{@code false} if not.
  392.      * @throws IOException If a network I/O error occurs.
  393.      */
  394.     public boolean logout() throws IOException {
  395.         return doCommand(IMAPCommand.LOGOUT);
  396.     }

  397.     /**
  398.      * Send an LSUB command to the server. Quotes the parameters if necessary.
  399.      *
  400.      * @param refName     The reference name.
  401.      * @param mailboxName The mailbox name.
  402.      * @return {@code true} if the command was successful,{@code false} if not.
  403.      * @throws IOException If a network I/O error occurs.
  404.      */
  405.     public boolean lsub(final String refName, final String mailboxName) throws IOException {
  406.         return doCommand(IMAPCommand.LSUB, quoteMailboxName(refName) + " " + quoteMailboxName(mailboxName));
  407.     }

  408.     /**
  409.      * 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.
  410.      *
  411.      * @return {@code true} if the command was successful,{@code false} if not.
  412.      * @throws IOException If a network I/O error occurs.
  413.      */
  414.     public boolean noop() throws IOException {
  415.         return doCommand(IMAPCommand.NOOP);
  416.     }

  417.     /**
  418.      * Send a RENAME command to the server.
  419.      *
  420.      * @param oldMailboxName The existing mailbox name to rename.
  421.      * @param newMailboxName The new mailbox name.
  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 rename(final String oldMailboxName, final String newMailboxName) throws IOException {
  426.         return doCommand(IMAPCommand.RENAME, quoteMailboxName(oldMailboxName) + " " + quoteMailboxName(newMailboxName));
  427.     }

  428.     /**
  429.      * Send a SEARCH command to the server.
  430.      *
  431.      * @param criteria The search criteria.
  432.      * @return {@code true} if the command was successful,{@code false} if not.
  433.      * @throws IOException If a network I/O error occurs.
  434.      */
  435.     public boolean search(final String criteria) throws IOException {
  436.         return search(null, criteria);
  437.     }

  438.     /**
  439.      * Send a SEARCH command to the server.
  440.      *
  441.      * @param charset  The charset (optional).
  442.      * @param criteria The search criteria.
  443.      * @return {@code true} if the command was successful,{@code false} if not.
  444.      * @throws IOException If a network I/O error occurs.
  445.      */
  446.     public boolean search(final String charset, final String criteria) throws IOException {
  447.         final StringBuilder args = new StringBuilder();
  448.         if (charset != null) {
  449.             args.append("CHARSET ").append(charset);
  450.         }
  451.         args.append(criteria);
  452.         return doCommand(IMAPCommand.SEARCH, args.toString());
  453.     }

  454.     /**
  455.      * Send a SELECT command to the server.
  456.      *
  457.      * @param mailboxName The mailbox name to select.
  458.      * @return {@code true} if the command was successful,{@code false} if not.
  459.      * @throws IOException If a network I/O error occurs.
  460.      */
  461.     public boolean select(final String mailboxName) throws IOException {
  462.         return doCommand(IMAPCommand.SELECT, quoteMailboxName(mailboxName));
  463.     }

  464.     /**
  465.      * Send a STATUS command to the server.
  466.      *
  467.      * @param mailboxName The reference name.
  468.      * @param itemNames   The status data item names.
  469.      * @return {@code true} if the command was successful,{@code false} if not.
  470.      * @throws IOException If a network I/O error occurs.
  471.      */
  472.     public boolean status(final String mailboxName, final String[] itemNames) throws IOException {
  473.         if (itemNames == null || itemNames.length < 1) {
  474.             throw new IllegalArgumentException("STATUS command requires at least one data item name");
  475.         }

  476.         final StringBuilder sb = new StringBuilder();
  477.         sb.append(quoteMailboxName(mailboxName));

  478.         sb.append(" (");
  479.         for (int i = 0; i < itemNames.length; i++) {
  480.             if (i > 0) {
  481.                 sb.append(" ");
  482.             }
  483.             sb.append(itemNames[i]);
  484.         }
  485.         sb.append(")");

  486.         return doCommand(IMAPCommand.STATUS, sb.toString());
  487.     }

  488.     /**
  489.      * Send a STORE command to the server.
  490.      *
  491.      * @param sequenceSet The sequence set to update (e.g. 2:5)
  492.      * @param itemNames   The item name for the STORE command (i.e. [+|-]FLAGS[.SILENT])
  493.      * @param itemValues  The item values for the STORE command. (e.g. (\Deleted) )
  494.      * @return {@code true} if the command was successful,{@code false} if not.
  495.      * @throws IOException If a network I/O error occurs.
  496.      */
  497.     public boolean store(final String sequenceSet, final String itemNames, final String itemValues) throws IOException {
  498.         return doCommand(IMAPCommand.STORE, sequenceSet + " " + itemNames + " " + itemValues);
  499.     }

  500.     /**
  501.      * Send a SUBSCRIBE command to the server.
  502.      *
  503.      * @param mailboxName The mailbox name to subscribe to.
  504.      * @return {@code true} if the command was successful,{@code false} if not.
  505.      * @throws IOException If a network I/O error occurs.
  506.      */
  507.     public boolean subscribe(final String mailboxName) throws IOException {
  508.         return doCommand(IMAPCommand.SUBSCRIBE, quoteMailboxName(mailboxName));
  509.     }

  510.     /**
  511.      * Send a UID command to the server.
  512.      *
  513.      * @param command     The command for UID.
  514.      * @param commandArgs The arguments for the command.
  515.      * @return {@code true} if the command was successful,{@code false} if not.
  516.      * @throws IOException If a network I/O error occurs.
  517.      */
  518.     public boolean uid(final String command, final String commandArgs) throws IOException {
  519.         return doCommand(IMAPCommand.UID, command + " " + commandArgs);
  520.     }

  521.     /**
  522.      * Send a UNSUBSCRIBE command to the server.
  523.      *
  524.      * @param mailboxName The mailbox name to unsubscribe from.
  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 unsubscribe(final String mailboxName) throws IOException {
  529.         return doCommand(IMAPCommand.UNSUBSCRIBE, quoteMailboxName(mailboxName));
  530.     }

  531. }