View Javadoc
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  
18  package org.apache.commons.net.imap;
19  
20  import java.io.IOException;
21  
22  /**
23   * The IMAPClient class provides the basic functionalities found in an IMAP client.
24   */
25  public class IMAPClient extends IMAP {
26  
27      /**
28       * The message data item names for the FETCH command defined in RFC 3501.
29       */
30      public enum FETCH_ITEM_NAMES {
31          /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE). */
32          ALL,
33          /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE). */
34          FAST,
35          /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY). */
36          FULL,
37          /** Non-extensible form of BODYSTRUCTURE or the text of a particular body section. */
38          BODY,
39          /** The [MIME-IMB] body structure of the message. */
40          BODYSTRUCTURE,
41          /** The envelope structure of the message. */
42          ENVELOPE,
43          /** The flags that are set for this message. */
44          FLAGS,
45          /** The internal date of the message. */
46          INTERNALDATE,
47          /** A prefix for RFC-822 item names. */
48          RFC822,
49          /** The unique identifier for the message. */
50          UID
51      }
52  
53      /**
54       * The search criteria defined in RFC 3501.
55       */
56      public enum SEARCH_CRITERIA {
57          /** All messages in the mailbox. */
58          ALL,
59          /** Messages with the \Answered flag set. */
60          ANSWERED,
61          /**
62           * Messages that contain the specified string in the envelope structure's BCC field.
63           */
64          BCC,
65          /**
66           * Messages whose internal date (disregarding time and time zone) is earlier than the specified date.
67           */
68          BEFORE,
69          /**
70           * Messages that contain the specified string in the body of the message.
71           */
72          BODY,
73          /**
74           * Messages that contain the specified string in the envelope structure's CC field.
75           */
76          CC,
77          /** Messages with the \Deleted flag set. */
78          DELETED,
79          /** Messages with the \Draft flag set. */
80          DRAFT,
81          /** Messages with the \Flagged flag set. */
82          FLAGGED,
83          /**
84           * Messages that contain the specified string in the envelope structure's FROM field.
85           */
86          FROM,
87          /**
88           * 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
89           * (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
90           * regardless of the contents.
91           */
92          HEADER,
93          /** Messages with the specified keyword flag set. */
94          KEYWORD,
95          /**
96           * Messages with an [RFC-2822] size larger than the specified number of octets.
97           */
98          LARGER,
99          /**
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