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.  *      http://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.      * Send an APPEND command to the server.
  186.      *
  187.      * @param mailboxName The mailbox name.
  188.      * @return {@code true} if the command was successful,{@code false} if not.
  189.      * @throws IOException If a network I/O error occurs.
  190.      * @deprecated (3.4) Does not work; the message body is not optional. Use {@link #append(String, String, String, String)} instead.
  191.      */
  192.     @Deprecated
  193.     public boolean append(final String mailboxName) throws IOException {
  194.         return append(mailboxName, null, null);
  195.     }

  196.     // --------- commands available in the authenticated state

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

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

  257.     /**
  258.      * Send a CAPABILITY command to the server.
  259.      *
  260.      * @return {@code true} if the command was successful,{@code false} if not.
  261.      * @throws IOException If a network I/O error occurs
  262.      */
  263.     public boolean capability() throws IOException {
  264.         return doCommand(IMAPCommand.CAPABILITY);
  265.     }

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

  275.     /**
  276.      * Send a CLOSE command to the server.
  277.      *
  278.      * @return {@code true} if the command was successful,{@code false} if not.
  279.      * @throws IOException If a network I/O error occurs.
  280.      */
  281.     public boolean close() throws IOException {
  282.         return doCommand(IMAPCommand.CLOSE);
  283.     }

  284.     /**
  285.      * Send a COPY command to the server.
  286.      *
  287.      * @param sequenceSet The sequence set to fetch.
  288.      * @param mailboxName The mailbox name.
  289.      * @return {@code true} if the command was successful,{@code false} if not.
  290.      * @throws IOException If a network I/O error occurs.
  291.      */
  292.     public boolean copy(final String sequenceSet, final String mailboxName) throws IOException {
  293.         return doCommand(IMAPCommand.COPY, sequenceSet + " " + quoteMailboxName(mailboxName));
  294.     }

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

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

  315.     /**
  316.      * Send an EXAMINE command to the server.
  317.      *
  318.      * @param mailboxName The mailbox name to examine.
  319.      * @return {@code true} if the command was successful,{@code false} if not.
  320.      * @throws IOException If a network I/O error occurs.
  321.      */
  322.     public boolean examine(final String mailboxName) throws IOException {
  323.         return doCommand(IMAPCommand.EXAMINE, quoteMailboxName(mailboxName));
  324.     }

  325.     /**
  326.      * Send an EXPUNGE command to the server.
  327.      *
  328.      * @return {@code true} if the command was successful,{@code false} if not.
  329.      * @throws IOException If a network I/O error occurs.
  330.      */
  331.     public boolean expunge() throws IOException {
  332.         return doCommand(IMAPCommand.EXPUNGE);
  333.     }

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

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

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

  374.         if (!doCommand(IMAPCommand.LOGIN, user + " " + password)) {
  375.             return false;
  376.         }

  377.         setState(IMAP.IMAPState.AUTH_STATE);

  378.         return true;
  379.     }

  380.     // --------- commands available in the selected state

  381.     /**
  382.      * 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
  383.      * client is in the not authenticated or authenticated state, it enters the logout on a successful logout.
  384.      *
  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 logout() throws IOException {
  389.         return doCommand(IMAPCommand.LOGOUT);
  390.     }

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

  402.     /**
  403.      * 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.
  404.      *
  405.      * @return {@code true} if the command was successful,{@code false} if not.
  406.      * @throws IOException If a network I/O error occurs.
  407.      */
  408.     public boolean noop() throws IOException {
  409.         return doCommand(IMAPCommand.NOOP);
  410.     }

  411.     /**
  412.      * Send a RENAME command to the server.
  413.      *
  414.      * @param oldMailboxName The existing mailbox name to rename.
  415.      * @param newMailboxName The new mailbox name.
  416.      * @return {@code true} if the command was successful,{@code false} if not.
  417.      * @throws IOException If a network I/O error occurs.
  418.      */
  419.     public boolean rename(final String oldMailboxName, final String newMailboxName) throws IOException {
  420.         return doCommand(IMAPCommand.RENAME, quoteMailboxName(oldMailboxName) + " " + quoteMailboxName(newMailboxName));
  421.     }

  422.     /**
  423.      * Send a SEARCH command to the server.
  424.      *
  425.      * @param criteria The search criteria.
  426.      * @return {@code true} if the command was successful,{@code false} if not.
  427.      * @throws IOException If a network I/O error occurs.
  428.      */
  429.     public boolean search(final String criteria) throws IOException {
  430.         return search(null, criteria);
  431.     }

  432.     /**
  433.      * Send a SEARCH command to the server.
  434.      *
  435.      * @param charset  The charset (optional).
  436.      * @param criteria The search criteria.
  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 search(final String charset, final String criteria) throws IOException {
  441.         final StringBuilder args = new StringBuilder();
  442.         if (charset != null) {
  443.             args.append("CHARSET ").append(charset);
  444.         }
  445.         args.append(criteria);
  446.         return doCommand(IMAPCommand.SEARCH, args.toString());
  447.     }

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

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

  470.         final StringBuilder sb = new StringBuilder();
  471.         sb.append(quoteMailboxName(mailboxName));

  472.         sb.append(" (");
  473.         for (int i = 0; i < itemNames.length; i++) {
  474.             if (i > 0) {
  475.                 sb.append(" ");
  476.             }
  477.             sb.append(itemNames[i]);
  478.         }
  479.         sb.append(")");

  480.         return doCommand(IMAPCommand.STATUS, sb.toString());
  481.     }

  482.     /**
  483.      * Send a STORE command to the server.
  484.      *
  485.      * @param sequenceSet The sequence set to update (e.g. 2:5)
  486.      * @param itemNames   The item name for the STORE command (i.e. [+|-]FLAGS[.SILENT])
  487.      * @param itemValues  The item values for the STORE command. (e.g. (\Deleted) )
  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 store(final String sequenceSet, final String itemNames, final String itemValues) throws IOException {
  492.         return doCommand(IMAPCommand.STORE, sequenceSet + " " + itemNames + " " + itemValues);
  493.     }

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

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

  515.     /**
  516.      * Send a UNSUBSCRIBE command to the server.
  517.      *
  518.      * @param mailboxName The mailbox name to unsubscribe from.
  519.      * @return {@code true} if the command was successful,{@code false} if not.
  520.      * @throws IOException If a network I/O error occurs.
  521.      */
  522.     public boolean unsubscribe(final String mailboxName) throws IOException {
  523.         return doCommand(IMAPCommand.UNSUBSCRIBE, quoteMailboxName(mailboxName));
  524.     }

  525. }