196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/*
296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * you may not use this file except in compliance with the License.
696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * You may obtain a copy of the License at
796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
1096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * See the License for the specific language governing permissions and
1496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * limitations under the License.
1596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */
1696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
1796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpackage com.android.email.mail.store;
1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
19627bc6ed57ee06cc588e64ff959bfd7870b659b6Marc Blankimport android.content.Context;
20627bc6ed57ee06cc588e64ff959bfd7870b659b6Marc Blankimport android.os.Bundle;
21627bc6ed57ee06cc588e64ff959bfd7870b659b6Marc Blankimport android.util.Log;
22627bc6ed57ee06cc588e64ff959bfd7870b659b6Marc Blank
23e71a19a52313a0fb615700e52a336b65f3c305daBen Komaloimport com.android.email.Controller;
2496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport com.android.email.Email;
2596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport com.android.email.mail.Store;
2696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport com.android.email.mail.Transport;
2796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport com.android.email.mail.transport.MailTransport;
2831d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blankimport com.android.emailcommon.Logging;
292193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.internet.MimeMessage;
302193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.mail.AuthenticationFailedException;
312193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.mail.FetchProfile;
322193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.mail.Flag;
332193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.mail.Folder;
34627bc6ed57ee06cc588e64ff959bfd7870b659b6Marc Blankimport com.android.emailcommon.mail.Folder.OpenMode;
352193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.mail.Message;
362193962ca2b3157e79f731736afa2a0c972e778aMarc Blankimport com.android.emailcommon.mail.MessagingException;
37f5418f1f93b02e7fab9f15eb201800b65510998eMarc Blankimport com.android.emailcommon.provider.Account;
3812b82d9374947c9268217f45befe8a74bd9b60d7Ben Komaloimport com.android.emailcommon.provider.HostAuth;
3953ea83ebf91f820692e8fa8e781f5cc982dd94dbBen Komaloimport com.android.emailcommon.provider.Mailbox;
400d4fc55861ed4393aa82f124f2865695ef564641Marc Blankimport com.android.emailcommon.service.EmailServiceProxy;
41627bc6ed57ee06cc588e64ff959bfd7870b659b6Marc Blankimport com.android.emailcommon.service.SearchParams;
4231d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blankimport com.android.emailcommon.utility.LoggingInputStream;
4331d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blankimport com.android.emailcommon.utility.Utility;
44745b33b8ff55e9a9c4871f07f9d97db893f784b2Makoto Onukiimport com.google.common.annotations.VisibleForTesting;
4596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
4696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.IOException;
4796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStream;
4896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.ArrayList;
4996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.HashMap;
5096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.HashSet;
5196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
5296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpublic class Pop3Store extends Store {
5396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    // All flags defining debug or development code settings must be FALSE
5496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    // when code is checked in or released.
5596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static boolean DEBUG_FORCE_SINGLE_LINE_UIDL = false;
563469902379242c723b871d1dcb09b02d0998d538The Android Open Source Project    private static boolean DEBUG_LOG_RAW_STREAM = false;
57dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki
5896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED };
5922208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy    /** The name of the only mailbox available to POP3 accounts */
6022208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy    private static final String POP3_MAILBOX_NAME = "INBOX";
61b3f7dd0169a35221184b9327c8ce337b09dc6d1fMakoto Onuki    private final HashMap<String, Folder> mFolders = new HashMap<String, Folder>();
6296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
6396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//    /**
6496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * Detected latency, used for usage scaling.
65171c3f2273223652b9999977d530a715420c0f64Todd Kennedy//     * Usage scaling occurs when it is necessary to get information about
6696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * messages that could result in large data loads. This value allows
6796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * the code that loads this data to decide between using large downloads
6896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * (high latency) or multiple round trips (low latency) to accomplish
6996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * the same thing.
7096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * Default is Integer.MAX_VALUE implying massive latency so that the large
7196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * download method is used by default until latency data is collected.
7296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     */
7396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//    private int mLatencyMs = Integer.MAX_VALUE;
7496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//
7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//    /**
7696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * Detected throughput, used for usage scaling.
77171c3f2273223652b9999977d530a715420c0f64Todd Kennedy//     * Usage scaling occurs when it is necessary to get information about
7896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * messages that could result in large data loads. This value allows
7996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * the code that loads this data to decide between using large downloads
8096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * (high latency) or multiple round trips (low latency) to accomplish
8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * the same thing.
8296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * Default is Integer.MAX_VALUE implying massive bandwidth so that the
8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * large download method is used by default until latency data is
8496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     * collected.
8596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//     */
8696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//    private int mThroughputKbS = Integer.MAX_VALUE;
8796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
8896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
89ae8ca3fbd1545c3a94011d7d70bcadac99e7779fAndy Stadler     * Static named constructor.
90ae8ca3fbd1545c3a94011d7d70bcadac99e7779fAndy Stadler     */
9135b0e95ca795e17b6dc8dd98c7ab847d65d9aa0cMarc Blank    public static Store newInstance(Account account, Context context) throws MessagingException {
92a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        return new Pop3Store(context, account);
93ae8ca3fbd1545c3a94011d7d70bcadac99e7779fAndy Stadler    }
94ae8ca3fbd1545c3a94011d7d70bcadac99e7779fAndy Stadler
95ae8ca3fbd1545c3a94011d7d70bcadac99e7779fAndy Stadler    /**
96a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy     * Creates a new store for the given account.
9796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
98a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy    private Pop3Store(Context context, Account account) throws MessagingException {
99200c6bd9fa19b78acc2c1664f858521aa9885353Todd Kennedy        mContext = context;
100200c6bd9fa19b78acc2c1664f858521aa9885353Todd Kennedy        mAccount = account;
101200c6bd9fa19b78acc2c1664f858521aa9885353Todd Kennedy
102a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
1036fea021e3dbe71aaa93512aba699d1bfda3d520dMarc Blank        if (recvAuth == null || !HostAuth.SCHEME_POP3.equalsIgnoreCase(recvAuth.mProtocol)) {
104e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler            throw new MessagingException("Unsupported protocol");
105e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler        }
106e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler        // defaults, which can be changed by security modifiers
10796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int connectionSecurity = Transport.CONNECTION_SECURITY_NONE;
108e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler        int defaultPort = 110;
109a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy
110a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        // check for security flags and apply changes
111a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        if ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) {
112e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler            connectionSecurity = Transport.CONNECTION_SECURITY_SSL;
11396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            defaultPort = 995;
114a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        } else if ((recvAuth.mFlags & HostAuth.FLAG_TLS) != 0) {
115e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler            connectionSecurity = Transport.CONNECTION_SECURITY_TLS;
11696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
117a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        boolean trustCertificates = ((recvAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0);
118e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler
119a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        int port = defaultPort;
120a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        if (recvAuth.mPort != HostAuth.PORT_UNKNOWN) {
121a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy            port = recvAuth.mPort;
122a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        }
12396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mTransport = new MailTransport("POP3");
124a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        mTransport.setHost(recvAuth.mAddress);
125a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        mTransport.setPort(port);
126e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler        mTransport.setSecurity(connectionSecurity, trustCertificates);
12796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
128a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        String[] userInfoParts = recvAuth.getLogin();
12996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (userInfoParts != null) {
13096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mUsername = userInfoParts[0];
131a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy            mPassword = userInfoParts[1];
13296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
13396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
134dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki
13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * For testing only.  Injects a different transport.  The transport should already be set
13796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * up and ready to use.  Do not use for real code.
13896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param testTransport The Transport to inject and use for all future communication.
13996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
14096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /* package */ void setTransport(Transport testTransport) {
14196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mTransport = testTransport;
14296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
14396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
14496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    @Override
145a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy    public Folder getFolder(String name) {
14696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Folder folder = mFolders.get(name);
14796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (folder == null) {
14896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            folder = new Pop3Folder(name);
14996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mFolders.put(folder.getName(), folder);
15096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
15196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return folder;
15296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
15396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
154e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo    private final int[] DEFAULT_FOLDERS = {
155e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo            Mailbox.TYPE_DRAFTS,
156e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo            Mailbox.TYPE_OUTBOX,
157e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo            Mailbox.TYPE_SENT,
158e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo            Mailbox.TYPE_TRASH
159e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo    };
160e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo
16196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    @Override
16222208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy    public Folder[] updateFolders() {
1630b8e04c84def3a06ef45126b48efc485fa0a7628Marc Blank        Mailbox mailbox = Mailbox.getMailboxForPath(mContext, mAccount.mId, POP3_MAILBOX_NAME);
164019341af98ffe2dcd484bd0468c9858d9e7cd7a3Todd Kennedy        updateMailbox(mailbox, mAccount.mId, POP3_MAILBOX_NAME, '\0', true, Mailbox.TYPE_INBOX);
165e07ecb8864b2b77270a1fa4844d6614c93c7291bTodd Kennedy        // Force the parent key to be "no mailbox" for the mail POP3 mailbox
166e07ecb8864b2b77270a1fa4844d6614c93c7291bTodd Kennedy        mailbox.mParentKey = Mailbox.NO_MAILBOX;
16722208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy        if (mailbox.isSaved()) {
16822208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy            mailbox.update(mContext, mailbox.toContentValues());
16922208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy        } else {
17022208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy            mailbox.save(mContext);
17122208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy        }
172e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo
173e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo        // Build default mailboxes as well, in case they're not already made.
174e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo        for (int type : DEFAULT_FOLDERS) {
175e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo            if (Mailbox.findMailboxOfType(mContext, mAccount.mId, type) == Mailbox.NO_MAILBOX) {
176e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo                String name = Controller.getMailboxServerName(mContext, type);
177e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo                Mailbox.newSystemMailbox(mAccount.mId, type, name).save(mContext);
178e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo            }
179e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo        }
180e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo
18122208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy        return new Folder[] { getFolder(POP3_MAILBOX_NAME) };
18296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
18396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
18496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
18596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Used by account setup to test if an account's settings are appropriate.  The definition
18696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * of "checked" here is simply, can you log into the account and does it meet some minimum set
18796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * of feature requirements?
188dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki     *
18996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @throws MessagingException if there was some problem with the account
19096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
19196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    @Override
192d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank    public Bundle checkSettings() throws MessagingException {
19322208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy        Pop3Folder folder = new Pop3Folder(POP3_MAILBOX_NAME);
194d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank        Bundle bundle = null;
19554ab7a27a3756a241ba06f0762969cf5a338bf60Andy Stadler        // Close any open or half-open connections - checkSettings should always be "fresh"
19654ab7a27a3756a241ba06f0762969cf5a338bf60Andy Stadler        if (mTransport.isOpen()) {
19754ab7a27a3756a241ba06f0762969cf5a338bf60Andy Stadler            folder.close(false);
19854ab7a27a3756a241ba06f0762969cf5a338bf60Andy Stadler        }
19996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        try {
200244d306ebb0addbc5d87008af9412b76c80e861eMarc Blank            folder.open(OpenMode.READ_WRITE);
201d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank            bundle = folder.checkSettings();
20296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        } finally {
20396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            folder.close(false);    // false == don't expunge anything
20496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
205d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank        return bundle;
20696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
20796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
20896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    class Pop3Folder extends Folder {
209b3f7dd0169a35221184b9327c8ce337b09dc6d1fMakoto Onuki        private final HashMap<String, Pop3Message> mUidToMsgMap
210b3f7dd0169a35221184b9327c8ce337b09dc6d1fMakoto Onuki                = new HashMap<String, Pop3Message>();
211b3f7dd0169a35221184b9327c8ce337b09dc6d1fMakoto Onuki        private final HashMap<Integer, Pop3Message> mMsgNumToMsgMap
212b3f7dd0169a35221184b9327c8ce337b09dc6d1fMakoto Onuki                = new HashMap<Integer, Pop3Message>();
213b3f7dd0169a35221184b9327c8ce337b09dc6d1fMakoto Onuki        private final HashMap<String, Integer> mUidToMsgNumMap = new HashMap<String, Integer>();
214b3f7dd0169a35221184b9327c8ce337b09dc6d1fMakoto Onuki        private final String mName;
21596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        private int mMessageCount;
21696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        private Pop3Capabilities mCapabilities;
21796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
21896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public Pop3Folder(String name) {
21922208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy            if (name.equalsIgnoreCase(POP3_MAILBOX_NAME)) {
22022208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy                mName = POP3_MAILBOX_NAME;
221b3f7dd0169a35221184b9327c8ce337b09dc6d1fMakoto Onuki            } else {
222b3f7dd0169a35221184b9327c8ce337b09dc6d1fMakoto Onuki                mName = name;
22396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
22496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
225dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki
22696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /**
22796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * Used by account setup to test if an account's settings are appropriate.  Here, we run
22896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * an additional test to see if UIDL is supported on the server. If it's not we
22996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * can't service this account.
230dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki         *
231d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank         * @return Bundle containing validation data (code and, if appropriate, error message)
23296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @throws MessagingException if the account is not going to be useable
23396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
234d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank        public Bundle checkSettings() throws MessagingException {
235d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank            Bundle bundle = new Bundle();
236d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank            int result = MessagingException.NO_ERROR;
23796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (!mCapabilities.uidl) {
23896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                try {
23996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    UidlParser parser = new UidlParser();
24096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    executeSimpleCommand("UIDL");
24196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    // drain the entire output, so additional communications don't get confused.
24296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    String response;
24396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    while ((response = mTransport.readLine()) != null) {
24496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        parser.parseMultiLine(response);
24596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        if (parser.mEndOfMessage) {
24696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                            break;
24796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        }
24896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
24996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                } catch (IOException ioe) {
25096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    mTransport.close();
251d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank                    result = MessagingException.IOERROR;
252d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank                    bundle.putString(EmailServiceProxy.VALIDATE_BUNDLE_ERROR_MESSAGE,
253d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank                            ioe.getMessage());
25496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
25596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
256d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank            bundle.putInt(EmailServiceProxy.VALIDATE_BUNDLE_RESULT_CODE, result);
257d6d874f8c6ce2580ef9ec2406fe411af45b2d92dMarc Blank            return bundle;
25896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
25996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
26096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
261244d306ebb0addbc5d87008af9412b76c80e861eMarc Blank        public synchronized void open(OpenMode mode) throws MessagingException {
26296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (mTransport.isOpen()) {
26396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return;
26496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
26596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
26622208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy            if (!mName.equalsIgnoreCase(POP3_MAILBOX_NAME)) {
26796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new MessagingException("Folder does not exist");
26896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
26996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
27096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            try {
27196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                mTransport.open();
27296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
27396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                // Eat the banner
27496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                executeSimpleCommand(null);
27596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
27696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                mCapabilities = getCapabilities();
27796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
27896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (mTransport.canTryTlsSecurity()) {
27996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (mCapabilities.stls) {
2803b85e2c2b5662c58525baa41479e42c59e292f66The Android Open Source Project                        executeSimpleCommand("STLS");
28196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        mTransport.reopenTls();
282e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler                    } else {
2838d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy                        if (Email.DEBUG) {
28431d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                            Log.d(Logging.LOG_TAG, "TLS not supported but required");
28596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        }
28696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        throw new MessagingException(MessagingException.TLS_REQUIRED);
28796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
28896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
28996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
29096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                try {
29196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    executeSensitiveCommand("USER " + mUsername, "USER /redacted/");
29296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    executeSensitiveCommand("PASS " + mPassword, "PASS /redacted/");
29396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                } catch (MessagingException me) {
2948d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy                    if (Email.DEBUG) {
29531d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                        Log.d(Logging.LOG_TAG, me.toString());
29696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
29796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    throw new AuthenticationFailedException(null, me);
29896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
29996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            } catch (IOException ioe) {
300092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                mTransport.close();
3018d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy                if (Email.DEBUG) {
30231d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                    Log.d(Logging.LOG_TAG, ioe.toString());
30396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
30496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new MessagingException(MessagingException.IOERROR, ioe.toString());
30596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
30696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
307092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler            Exception statException = null;
30896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            try {
30996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                String response = executeSimpleCommand("STAT");
31096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                String[] parts = response.split(" ");
311092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                if (parts.length < 2) {
312092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                    statException = new IOException();
313092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                } else {
314092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                    mMessageCount = Integer.parseInt(parts[1]);
315092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                }
316092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler            } catch (IOException ioe) {
317092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                statException = ioe;
318092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler            } catch (NumberFormatException nfe) {
319092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                statException = nfe;
32096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
321092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler            if (statException != null) {
322092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                mTransport.close();
3238d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy                if (Email.DEBUG) {
32431d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                    Log.d(Logging.LOG_TAG, statException.toString());
32596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
326092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                throw new MessagingException("POP3 STAT", statException);
32796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
32896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mUidToMsgMap.clear();
32996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mMsgNumToMsgMap.clear();
33096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mUidToMsgNumMap.clear();
33196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
33296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
33396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
334a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        public OpenMode getMode() {
3354f23ebd3dcca596533a33e6faedb446a814d32e3Takaoka G. Tadashi            return OpenMode.READ_WRITE;
33696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
33796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
33896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /**
339dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki         * Close the folder (and the transport below it).
340dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki         *
34196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * MUST NOT return any exceptions.
342dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki         *
34396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @param expunge If true all deleted messages will be expunged (TODO - not implemented)
34496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
34596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
34696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public void close(boolean expunge) {
34796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            try {
34896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                executeSimpleCommand("QUIT");
34996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
35096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            catch (Exception e) {
35196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                // ignore any problems here - just continue closing
35296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
35396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mTransport.close();
35496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
35596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
35696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
35796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public String getName() {
35896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return mName;
35996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
36096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
361c41c47fa07a22f8a7612fb0191f152a36d95b7a5Andrew Stadler        // POP3 does not folder creation
362165e8bfe7a46564ea98bb16cb059102ba356a1b5Makoto Onuki        @Override
363c41c47fa07a22f8a7612fb0191f152a36d95b7a5Andrew Stadler        public boolean canCreate(FolderType type) {
364c41c47fa07a22f8a7612fb0191f152a36d95b7a5Andrew Stadler            return false;
365c41c47fa07a22f8a7612fb0191f152a36d95b7a5Andrew Stadler        }
366c41c47fa07a22f8a7612fb0191f152a36d95b7a5Andrew Stadler
36796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
368a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        public boolean create(FolderType type) {
36996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return false;
37096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
37196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
37296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
373a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        public boolean exists() {
37422208771b7b39c5d131372ba6bc45ab23cc22232Todd Kennedy            return mName.equalsIgnoreCase(POP3_MAILBOX_NAME);
37596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
37696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
37796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
37896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public int getMessageCount() {
37996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return mMessageCount;
38096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
38196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
38296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
383a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        public int getUnreadMessageCount() {
38496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return -1;
38596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
38696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
38796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
38896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public Message getMessage(String uid) throws MessagingException {
3894f23ebd3dcca596533a33e6faedb446a814d32e3Takaoka G. Tadashi            if (mUidToMsgNumMap.size() == 0) {
3904f23ebd3dcca596533a33e6faedb446a814d32e3Takaoka G. Tadashi                try {
3914f23ebd3dcca596533a33e6faedb446a814d32e3Takaoka G. Tadashi                    indexMsgNums(1, mMessageCount);
3924f23ebd3dcca596533a33e6faedb446a814d32e3Takaoka G. Tadashi                } catch (IOException ioe) {
3934f23ebd3dcca596533a33e6faedb446a814d32e3Takaoka G. Tadashi                    mTransport.close();
3944f23ebd3dcca596533a33e6faedb446a814d32e3Takaoka G. Tadashi                    if (Email.DEBUG) {
39531d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                        Log.d(Logging.LOG_TAG, "Unable to index during getMessage " + ioe);
3964f23ebd3dcca596533a33e6faedb446a814d32e3Takaoka G. Tadashi                    }
3974f23ebd3dcca596533a33e6faedb446a814d32e3Takaoka G. Tadashi                    throw new MessagingException("getMessages", ioe);
3984f23ebd3dcca596533a33e6faedb446a814d32e3Takaoka G. Tadashi                }
39996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
4004f23ebd3dcca596533a33e6faedb446a814d32e3Takaoka G. Tadashi            Pop3Message message = mUidToMsgMap.get(uid);
40196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return message;
40296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
40396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
40496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
40596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public Message[] getMessages(int start, int end, MessageRetrievalListener listener)
40696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throws MessagingException {
40796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (start < 1 || end < 1 || end < start) {
40896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new MessagingException(String.format("Invalid message set %d %d",
40996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        start, end));
41096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
41196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            try {
41296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                indexMsgNums(start, end);
41396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            } catch (IOException ioe) {
41496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                mTransport.close();
4158d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy                if (Email.DEBUG) {
41631d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                    Log.d(Logging.LOG_TAG, ioe.toString());
41796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
41896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new MessagingException("getMessages", ioe);
41996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
42096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            ArrayList<Message> messages = new ArrayList<Message>();
42196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            for (int msgNum = start; msgNum <= end; msgNum++) {
42296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                Pop3Message message = mMsgNumToMsgMap.get(msgNum);
42396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                messages.add(message);
42496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (listener != null) {
425dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki                    listener.messageRetrieved(message);
42696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
42796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
42896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return messages.toArray(new Message[messages.size()]);
42996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
43096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
43196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /**
43296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * Ensures that the given message set (from start to end inclusive)
43396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * has been queried so that uids are available in the local cache.
43496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @param start
43596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @param end
43696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @throws MessagingException
43796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @throws IOException
43896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
43996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        private void indexMsgNums(int start, int end)
44096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throws MessagingException, IOException {
44196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            int unindexedMessageCount = 0;
44296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            for (int msgNum = start; msgNum <= end; msgNum++) {
44396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (mMsgNumToMsgMap.get(msgNum) == null) {
44496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    unindexedMessageCount++;
44596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
44696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
44796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (unindexedMessageCount == 0) {
44896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return;
44996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
45096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            UidlParser parser = new UidlParser();
45196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (DEBUG_FORCE_SINGLE_LINE_UIDL ||
45296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    (unindexedMessageCount < 50 && mMessageCount > 5000)) {
45396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                /*
45496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 * In extreme cases we'll do a UIDL command per message instead of a bulk
45596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 * download.
45696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 */
45796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                for (int msgNum = start; msgNum <= end; msgNum++) {
45896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    Pop3Message message = mMsgNumToMsgMap.get(msgNum);
45996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (message == null) {
46096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        String response = executeSimpleCommand("UIDL " + msgNum);
461092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        if (!parser.parseSingleLine(response)) {
462092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                            throw new IOException();
463092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        }
46496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        message = new Pop3Message(parser.mUniqueId, this);
46596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        indexMessage(msgNum, message);
46696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
46796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
468092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler            } else {
46996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                String response = executeSimpleCommand("UIDL");
47096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                while ((response = mTransport.readLine()) != null) {
471092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                    if (!parser.parseMultiLine(response)) {
472092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        throw new IOException();
473092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                    }
47496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (parser.mEndOfMessage) {
47596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        break;
47696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
47796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    int msgNum = parser.mMessageNumber;
47896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (msgNum >= start && msgNum <= end) {
47996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        Pop3Message message = mMsgNumToMsgMap.get(msgNum);
48096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        if (message == null) {
48196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                            message = new Pop3Message(parser.mUniqueId, this);
48296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                            indexMessage(msgNum, message);
48396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        }
48496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
48596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
48696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
48796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
48896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
48996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        private void indexUids(ArrayList<String> uids)
49096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throws MessagingException, IOException {
49196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            HashSet<String> unindexedUids = new HashSet<String>();
49296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            for (String uid : uids) {
49396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (mUidToMsgMap.get(uid) == null) {
49496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    unindexedUids.add(uid);
49596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
49696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
49796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (unindexedUids.size() == 0) {
49896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return;
49996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
50096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            /*
50196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * If we are missing uids in the cache the only sure way to
50296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * get them is to do a full UIDL list. A possible optimization
50396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * would be trying UIDL for the latest X messages and praying.
50496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             */
50596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            UidlParser parser = new UidlParser();
50696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            String response = executeSimpleCommand("UIDL");
50796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            while ((response = mTransport.readLine()) != null) {
50896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                parser.parseMultiLine(response);
50996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (parser.mEndOfMessage) {
51096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    break;
51196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
51296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (unindexedUids.contains(parser.mUniqueId)) {
51396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    Pop3Message message = mUidToMsgMap.get(parser.mUniqueId);
51496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (message == null) {
51596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        message = new Pop3Message(parser.mUniqueId, this);
51696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
51796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    indexMessage(parser.mMessageNumber, message);
51896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
51996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
52096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
52196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
52296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /**
52396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * Simple parser class for UIDL messages.
524dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki         *
525dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki         * <p>NOTE:  In variance with RFC 1939, we allow multiple whitespace between the
526dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki         * message-number and unique-id fields.  This provides greater compatibility with some
52796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * non-compliant POP3 servers, e.g. mail.comcast.net.
52896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
52996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /* package */ class UidlParser {
530dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki
53196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            /**
53296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * Caller can read back message-number from this field
53396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             */
53496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            public int mMessageNumber;
53596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            /**
53696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * Caller can read back unique-id from this field
53796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             */
53896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            public String mUniqueId;
53996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            /**
54096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * True if the response was "end-of-message"
54196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             */
54296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            public boolean mEndOfMessage;
54396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            /**
54496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * True if an error was reported
54596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             */
54696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            public boolean mErr;
547dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki
54896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            /**
54996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * Construct & Initialize
55096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             */
55196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            public UidlParser() {
55296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                mErr = true;
55396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
554dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki
55596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            /**
55696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * Parse a single-line response.  This is returned from a command of the form
557dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki             * "UIDL msg-num" and will be formatted as: "+OK msg-num unique-id" or
55896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * "-ERR diagnostic text"
559dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki             *
56096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * @param response The string returned from the server
56196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * @return true if the string parsed as expected (e.g. no syntax problems)
56296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             */
56396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            public boolean parseSingleLine(String response) {
56496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                mErr = false;
565b011a812e05902d7e417b8c47acc90a592f4b412Andrew Stadler                if (response == null || response.length() == 0) {
566b011a812e05902d7e417b8c47acc90a592f4b412Andrew Stadler                    return false;
567b011a812e05902d7e417b8c47acc90a592f4b412Andrew Stadler                }
56896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                char first = response.charAt(0);
56996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (first == '+') {
57096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    String[] uidParts = response.split(" +");
57196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (uidParts.length >= 3) {
572092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        try {
573092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                            mMessageNumber = Integer.parseInt(uidParts[1]);
574092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        } catch (NumberFormatException nfe) {
575092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                            return false;
576092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        }
57796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        mUniqueId = uidParts[2];
57896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        mEndOfMessage = true;
57996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        return true;
58096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
58196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                } else if (first == '-') {
58296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    mErr = true;
58396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return true;
58496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
58596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return false;
58696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
587dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki
58896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            /**
58996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * Parse a multi-line response.  This is returned from a command of the form
59096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * "UIDL" and will be formatted as: "." or "msg-num unique-id".
591dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki             *
59296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * @param response The string returned from the server
59396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             * @return true if the string parsed as expected (e.g. no syntax problems)
59496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project             */
59596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            public boolean parseMultiLine(String response) {
59696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                mErr = false;
597b011a812e05902d7e417b8c47acc90a592f4b412Andrew Stadler                if (response == null || response.length() == 0) {
598b011a812e05902d7e417b8c47acc90a592f4b412Andrew Stadler                    return false;
599b011a812e05902d7e417b8c47acc90a592f4b412Andrew Stadler                }
60096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                char first = response.charAt(0);
60196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (first == '.') {
60296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    mEndOfMessage = true;
60396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return true;
60496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                } else {
60596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    String[] uidParts = response.split(" +");
60696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (uidParts.length >= 2) {
607092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        try {
608092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                            mMessageNumber = Integer.parseInt(uidParts[0]);
609092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        } catch (NumberFormatException nfe) {
610092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                            return false;
611092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        }
61296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        mUniqueId = uidParts[1];
61396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        mEndOfMessage = false;
61496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        return true;
61596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
61696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
61796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return false;
61896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
61996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
62096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
62196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        private void indexMessage(int msgNum, Pop3Message message) {
62296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mMsgNumToMsgMap.put(msgNum, message);
62396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mUidToMsgMap.put(message.getUid(), message);
62496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mUidToMsgNumMap.put(message.getUid(), msgNum);
62596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
62696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
62796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
628a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        public Message[] getMessages(String[] uids, MessageRetrievalListener listener) {
629a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy            throw new UnsupportedOperationException(
630a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy                    "Pop3Folder.getMessage(MessageRetrievalListener)");
63196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
63296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
63396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /**
63496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * Fetch the items contained in the FetchProfile into the given set of
63596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * Messages in as efficient a manner as possible.
63696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @param messages
63796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @param fp
63896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @throws MessagingException
63996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
640165e8bfe7a46564ea98bb16cb059102ba356a1b5Makoto Onuki        @Override
64196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public void fetch(Message[] messages, FetchProfile fp, MessageRetrievalListener listener)
64296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throws MessagingException {
64396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (messages == null || messages.length == 0) {
64496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return;
64596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
64696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            ArrayList<String> uids = new ArrayList<String>();
64796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            for (Message message : messages) {
64896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                uids.add(message.getUid());
64996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
65096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            try {
65196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                indexUids(uids);
65296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (fp.contains(FetchProfile.Item.ENVELOPE)) {
65396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    // Note: We never pass the listener for the ENVELOPE call, because we're going
65496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    // to be calling the listener below in the per-message loop.
65596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    fetchEnvelope(messages, null);
65696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
657092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler            } catch (IOException ioe) {
65896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                mTransport.close();
6598d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy                if (Email.DEBUG) {
66031d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                    Log.d(Logging.LOG_TAG, ioe.toString());
66196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
66296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new MessagingException("fetch", ioe);
66396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
66496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            for (int i = 0, count = messages.length; i < count; i++) {
66596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                Message message = messages[i];
66696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (!(message instanceof Pop3Message)) {
66796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    throw new MessagingException("Pop3Store.fetch called with non-Pop3 Message");
66896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
66996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                Pop3Message pop3Message = (Pop3Message)message;
67096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                try {
67196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (fp.contains(FetchProfile.Item.BODY)) {
67296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        fetchBody(pop3Message, -1);
67396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
67496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    else if (fp.contains(FetchProfile.Item.BODY_SANE)) {
67596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        /*
67696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                         * To convert the suggested download size we take the size
67796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                         * divided by the maximum line size (76).
67896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                         */
67996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        fetchBody(pop3Message,
68096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                                FETCH_BODY_SANE_SUGGESTED_SIZE / 76);
68196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
68296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    else if (fp.contains(FetchProfile.Item.STRUCTURE)) {
68396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        /*
68496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                         * If the user is requesting STRUCTURE we are required to set the body
68596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                         * to null since we do not support the function.
68696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                         */
68796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        pop3Message.setBody(null);
68896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
68996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (listener != null) {
690dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki                        listener.messageRetrieved(message);
69196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
69296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                } catch (IOException ioe) {
69396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    mTransport.close();
6948d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy                    if (Email.DEBUG) {
69531d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                        Log.d(Logging.LOG_TAG, ioe.toString());
69696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
69796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    throw new MessagingException("Unable to fetch message", ioe);
69896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
69996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
70096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
70196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
70296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        private void fetchEnvelope(Message[] messages,
70396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                MessageRetrievalListener listener)  throws IOException, MessagingException {
70496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            int unsizedMessages = 0;
70596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            for (Message message : messages) {
70696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (message.getSize() == -1) {
70796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    unsizedMessages++;
70896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
70996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
71096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (unsizedMessages == 0) {
71196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return;
71296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
71396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (unsizedMessages < 50 && mMessageCount > 5000) {
71496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                /*
71596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 * In extreme cases we'll do a command per message instead of a bulk request
71696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 * to hopefully save some time and bandwidth.
71796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 */
71896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                for (int i = 0, count = messages.length; i < count; i++) {
71996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    Message message = messages[i];
72096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (!(message instanceof Pop3Message)) {
721e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo                        throw new MessagingException(
722e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo                                "Pop3Store.fetch called with non-Pop3 Message");
72396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
72496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    Pop3Message pop3Message = (Pop3Message)message;
72596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    String response = executeSimpleCommand(String.format("LIST %d",
72696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                            mUidToMsgNumMap.get(pop3Message.getUid())));
727092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                    try {
728092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        String[] listParts = response.split(" ");
729092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        int msgNum = Integer.parseInt(listParts[1]);
730092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        int msgSize = Integer.parseInt(listParts[2]);
731092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        pop3Message.setSize(msgSize);
732092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                    } catch (NumberFormatException nfe) {
733092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        throw new IOException();
734092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                    }
73596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (listener != null) {
736dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki                        listener.messageRetrieved(pop3Message);
73796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
73896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
739092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler            } else {
74096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                HashSet<String> msgUidIndex = new HashSet<String>();
74196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                for (Message message : messages) {
74296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    msgUidIndex.add(message.getUid());
74396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
74496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                String response = executeSimpleCommand("LIST");
74596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                while ((response = mTransport.readLine()) != null) {
74696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (response.equals(".")) {
74796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        break;
74896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
749092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                    Pop3Message pop3Message = null;
750092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                    int msgSize = 0;
751092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                    try {
752092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        String[] listParts = response.split(" ");
753092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        int msgNum = Integer.parseInt(listParts[0]);
754092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        msgSize = Integer.parseInt(listParts[1]);
755092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        pop3Message = mMsgNumToMsgMap.get(msgNum);
756092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                    } catch (NumberFormatException nfe) {
757092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                        throw new IOException();
758092d35c98d00c5a8b1ba93f51f8e59220f1488cbAndrew Stadler                    }
75996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (pop3Message != null && msgUidIndex.contains(pop3Message.getUid())) {
76096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        pop3Message.setSize(msgSize);
76196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        if (listener != null) {
762dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki                            listener.messageRetrieved(pop3Message);
76396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        }
76496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
76596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
76696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
76796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
76896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
76996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /**
77096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * Fetches the body of the given message, limiting the stored data
77196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * to the specified number of lines. If lines is -1 the entire message
77296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * is fetched. This is implemented with RETR for lines = -1 or TOP
77396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * for any other value. If the server does not support TOP it is
77496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * emulated with RETR and extra lines are thrown away.
775645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler         *
776645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler         * Note:  Some servers (e.g. live.com) don't support CAPA, but turn out to
777645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler         * support TOP after all.  For better performance on these servers, we'll always
778645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler         * probe TOP, and fall back to RETR when it's truly unsupported.
779645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler         *
78096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @param message
78196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @param lines
78296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
78396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        private void fetchBody(Pop3Message message, int lines)
78496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throws IOException, MessagingException {
78596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            String response = null;
786645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler            int messageId = mUidToMsgNumMap.get(message.getUid());
787645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler            if (lines == -1) {
788645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler                // Fetch entire message
789645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler                response = executeSimpleCommand(String.format("RETR %d", messageId));
790645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler            } else {
791645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler                // Fetch partial message.  Try "TOP", and fall back to slower "RETR" if necessary
792645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler                try {
793645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler                    response = executeSimpleCommand(String.format("TOP %d %d", messageId,  lines));
794645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler                } catch (MessagingException me) {
795645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler                    response = executeSimpleCommand(String.format("RETR %d", messageId));
796645fc2830118e19e604f24ba7ffe71c775631f64Andrew Stadler                }
79796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
79896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (response != null)  {
79996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                try {
8003469902379242c723b871d1dcb09b02d0998d538The Android Open Source Project                    InputStream in = mTransport.getInputStream();
8018d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy                    if (DEBUG_LOG_RAW_STREAM && Email.DEBUG) {
8023469902379242c723b871d1dcb09b02d0998d538The Android Open Source Project                        in = new LoggingInputStream(in);
8033469902379242c723b871d1dcb09b02d0998d538The Android Open Source Project                    }
8043469902379242c723b871d1dcb09b02d0998d538The Android Open Source Project                    message.parse(new Pop3ResponseInputStream(in));
80596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
80696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                catch (MessagingException me) {
80796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    /*
80896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                     * If we're only downloading headers it's possible
80996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                     * we'll get a broken MIME message which we're not
81096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                     * real worried about. If we've downloaded the body
81196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                     * and can't parse it we need to let the user know.
81296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                     */
81396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (lines == -1) {
81496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        throw me;
81596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
81696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
81796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
81896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
81996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
82096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
821a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        public Flag[] getPermanentFlags() {
82296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return PERMANENT_FLAGS;
82396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
82496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
825165e8bfe7a46564ea98bb16cb059102ba356a1b5Makoto Onuki        @Override
826a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        public void appendMessages(Message[] messages) {
82796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
82896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
829165e8bfe7a46564ea98bb16cb059102ba356a1b5Makoto Onuki        @Override
830a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        public void delete(boolean recurse) {
83196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
83296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
833165e8bfe7a46564ea98bb16cb059102ba356a1b5Makoto Onuki        @Override
834a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        public Message[] expunge() {
83596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return null;
83696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
83796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
838165e8bfe7a46564ea98bb16cb059102ba356a1b5Makoto Onuki        @Override
83996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public void setFlags(Message[] messages, Flag[] flags, boolean value)
84096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throws MessagingException {
84196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (!value || !Utility.arrayContains(flags, Flag.DELETED)) {
84296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                /*
84396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 * The only flagging we support is setting the Deleted flag.
84496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 */
84596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return;
84696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
84796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            try {
84896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                for (Message message : messages) {
84996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    executeSimpleCommand(String.format("DELE %s",
85096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                            mUidToMsgNumMap.get(message.getUid())));
85196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
85296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
85396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            catch (IOException ioe) {
85496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                mTransport.close();
8558d8537cd2e39268e0fdcd019bc8b6c4572b7c520Todd Kennedy                if (Email.DEBUG) {
85631d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank                    Log.d(Logging.LOG_TAG, ioe.toString());
85796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
85896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new MessagingException("setFlags()", ioe);
85996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
86096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
86196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
86296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
863a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        public void copyMessages(Message[] msgs, Folder folder, MessageUpdateCallbacks callbacks) {
86496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            throw new UnsupportedOperationException("copyMessages is not supported in POP3");
86596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
86696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
86796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//        private boolean isRoundTripModeSuggested() {
86896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//            long roundTripMethodMs =
86996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//                (uncachedMessageCount * 2 * mLatencyMs);
87096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//            long bulkMethodMs =
87196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//                    (mMessageCount * 58) / (mThroughputKbS * 1024 / 8) * 1000;
87296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//        }
87396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
874a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        private Pop3Capabilities getCapabilities() throws IOException {
87596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            Pop3Capabilities capabilities = new Pop3Capabilities();
87696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            try {
87796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                String response = executeSimpleCommand("CAPA");
87896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                while ((response = mTransport.readLine()) != null) {
87996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (response.equals(".")) {
88096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        break;
88196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
88296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (response.equalsIgnoreCase("STLS")){
88396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        capabilities.stls = true;
88496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
88596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    else if (response.equalsIgnoreCase("UIDL")) {
88696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        capabilities.uidl = true;
88796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
88896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    else if (response.equalsIgnoreCase("PIPELINING")) {
88996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        capabilities.pipelining = true;
89096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
89196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    else if (response.equalsIgnoreCase("USER")) {
89296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        capabilities.user = true;
89396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
89496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    else if (response.equalsIgnoreCase("TOP")) {
89596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        capabilities.top = true;
89696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
89796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
89896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
89996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            catch (MessagingException me) {
90096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                /*
90196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 * The server may not support the CAPA command, so we just eat this Exception
90296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 * and allow the empty capabilities object to be returned.
90396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 */
90496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
90596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return capabilities;
90696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
90796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
90896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /**
90996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * Send a single command and wait for a single line response.  Reopens the connection,
91096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * if it is closed.  Leaves the connection open.
911dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki         *
91296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @param command The command string to send to the server.
91396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @return Returns the response string from the server.
91496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
91596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        private String executeSimpleCommand(String command) throws IOException, MessagingException {
91696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return executeSensitiveCommand(command, null);
91796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
918dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki
91996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /**
92096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * Send a single command and wait for a single line response.  Reopens the connection,
92196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * if it is closed.  Leaves the connection open.
922dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki         *
92396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @param command The command string to send to the server.
92496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @param sensitiveReplacement If the command includes sensitive data (e.g. authentication)
92596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * please pass a replacement string here (for logging).
92696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * @return Returns the response string from the server.
92796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
92896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        private String executeSensitiveCommand(String command, String sensitiveReplacement)
92996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throws IOException, MessagingException {
930244d306ebb0addbc5d87008af9412b76c80e861eMarc Blank            open(OpenMode.READ_WRITE);
93196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
93296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (command != null) {
93396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                mTransport.writeLine(command, sensitiveReplacement);
93496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
93596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
93696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            String response = mTransport.readLine();
93796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
93896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (response.length() > 1 && response.charAt(0) == '-') {
93996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new MessagingException(response);
94096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
94196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
94296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return response;
94396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
94496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
94596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
94696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public boolean equals(Object o) {
94796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (o instanceof Pop3Folder) {
94896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return ((Pop3Folder) o).mName.equals(mName);
94996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
95096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return super.equals(o);
95196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
95296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
95396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
954745b33b8ff55e9a9c4871f07f9d97db893f784b2Makoto Onuki        @VisibleForTesting
9552720a818d5de169734434b114adfdf824a485f55Marc Blank        public boolean isOpen() {
95696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return mTransport.isOpen();
95796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
9580d1078363581db8caded06cf94e729e88a88761aAndrew Stadler
9590d1078363581db8caded06cf94e729e88a88761aAndrew Stadler        @Override
960a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        public Message createMessage(String uid) {
9610d1078363581db8caded06cf94e729e88a88761aAndrew Stadler            return new Pop3Message(uid, this);
9620d1078363581db8caded06cf94e729e88a88761aAndrew Stadler        }
963627bc6ed57ee06cc588e64ff959bfd7870b659b6Marc Blank
964627bc6ed57ee06cc588e64ff959bfd7870b659b6Marc Blank        @Override
965e71a19a52313a0fb615700e52a336b65f3c305daBen Komalo        public Message[] getMessages(SearchParams params, MessageRetrievalListener listener) {
966627bc6ed57ee06cc588e64ff959bfd7870b659b6Marc Blank            return null;
967627bc6ed57ee06cc588e64ff959bfd7870b659b6Marc Blank        }
96896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
96996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
970391ae25c43a38829cc0990af18c36bdc7cc374cfMarc Blank    public static class Pop3Message extends MimeMessage {
971a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        public Pop3Message(String uid, Pop3Folder folder) {
97296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mUid = uid;
97396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mFolder = folder;
97496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mSize = -1;
97596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
97696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
97796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public void setSize(int size) {
97896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mSize = size;
97996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
98096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
981165e8bfe7a46564ea98bb16cb059102ba356a1b5Makoto Onuki        @Override
982391ae25c43a38829cc0990af18c36bdc7cc374cfMarc Blank        public void parse(InputStream in) throws IOException, MessagingException {
98396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            super.parse(in);
98496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
98596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
98696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
98796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public void setFlag(Flag flag, boolean set) throws MessagingException {
98896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            super.setFlag(flag, set);
98996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mFolder.setFlags(new Message[] { this }, new Flag[] { flag }, set);
99096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
99196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
99296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
993dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki    /**
99496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * POP3 Capabilities as defined in RFC 2449.  This is not a complete list of CAPA
995dbbd2ba22c63f38c268c1c33e285cfcc50f6da27Makoto Onuki     * responses - just those that we use in this client.
99696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
99796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    class Pop3Capabilities {
99896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /** The STLS (start TLS) command is supported */
99996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public boolean stls;
100096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /** the TOP command (retrieve a partial message) is supported */
100196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public boolean top;
100296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /** USER and PASS login/auth commands are supported */
100396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public boolean user;
100496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /** the optional UIDL command is supported (unused) */
100596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public boolean uidl;
100696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /** the server is capable of accepting multiple commands at a time (unused) */
100796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public boolean pipelining;
100896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
1009165e8bfe7a46564ea98bb16cb059102ba356a1b5Makoto Onuki        @Override
101096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public String toString() {
101196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return String.format("STLS %b, TOP %b, USER %b, UIDL %b, PIPELINING %b",
101296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    stls,
101396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    top,
101496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    user,
101596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    uidl,
101696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    pipelining);
101796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
101896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
101996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
102096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    // TODO figure out what is special about this and merge it into MailTransport
102196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    class Pop3ResponseInputStream extends InputStream {
1022b3f7dd0169a35221184b9327c8ce337b09dc6d1fMakoto Onuki        private final InputStream mIn;
1023b3f7dd0169a35221184b9327c8ce337b09dc6d1fMakoto Onuki        private boolean mStartOfLine = true;
1024b3f7dd0169a35221184b9327c8ce337b09dc6d1fMakoto Onuki        private boolean mFinished;
102596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
102696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public Pop3ResponseInputStream(InputStream in) {
102796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mIn = in;
102896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
102996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
103096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
103196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public int read() throws IOException {
103296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (mFinished) {
103396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return -1;
103496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
103596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            int d = mIn.read();
103696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (mStartOfLine && d == '.') {
103796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                d = mIn.read();
103896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (d == '\r') {
103996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    mFinished = true;
104096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    mIn.read();
104196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return -1;
104296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
104396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
104496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
104596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mStartOfLine = (d == '\n');
104696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
104796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            return d;
104896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
104996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
105096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project}
1051