001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.net.imap;
019
020import java.io.IOException;
021
022/**
023 * The IMAPClient class provides the basic functionalities found in an IMAP client.
024 */
025public class IMAPClient extends IMAP {
026
027    /**
028     * The message data item names for the FETCH command defined in RFC 3501.
029     */
030    public enum FETCH_ITEM_NAMES {
031
032        /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE). */
033        ALL,
034
035        /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE). */
036        FAST,
037
038        /** Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY). */
039        FULL,
040
041        /** Non-extensible form of BODYSTRUCTURE or the text of a particular body section. */
042        BODY,
043
044        /** The [MIME-IMB] body structure of the message. */
045        BODYSTRUCTURE,
046
047        /** The envelope structure of the message. */
048        ENVELOPE,
049
050        /** The flags that are set for this message. */
051        FLAGS,
052
053        /** The internal date of the message. */
054        INTERNALDATE,
055
056        /** A prefix for RFC-822 item names. */
057        RFC822,
058
059        /** The unique identifier for the message. */
060        UID
061    }
062
063    /**
064     * The search criteria defined in RFC 3501.
065     */
066    public enum SEARCH_CRITERIA {
067
068        /** All messages in the mailbox. */
069        ALL,
070
071        /** Messages with the \Answered flag set. */
072        ANSWERED,
073
074        /**
075         * Messages that contain the specified string in the envelope structure's BCC field.
076         */
077        BCC,
078
079        /**
080         * Messages whose internal date (disregarding time and time zone) is earlier than the specified date.
081         */
082        BEFORE,
083
084        /**
085         * Messages that contain the specified string in the body of the message.
086         */
087        BODY,
088
089        /**
090         * Messages that contain the specified string in the envelope structure's CC field.
091         */
092        CC,
093
094        /** Messages with the \Deleted flag set. */
095        DELETED,
096
097        /** Messages with the \Draft flag set. */
098        DRAFT,
099
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