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