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