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