2009-03-04 03:32:22 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package com.android.email.mail.store;
|
|
|
|
|
2010-05-17 20:13:56 +00:00
|
|
|
import com.android.email.Email;
|
2010-05-17 22:44:36 +00:00
|
|
|
import com.android.email.Utility;
|
2010-04-02 18:09:12 +00:00
|
|
|
import com.android.email.mail.Address;
|
2010-05-17 22:44:36 +00:00
|
|
|
import com.android.email.mail.Body;
|
2009-10-07 18:42:27 +00:00
|
|
|
import com.android.email.mail.FetchProfile;
|
2009-05-20 17:36:16 +00:00
|
|
|
import com.android.email.mail.Flag;
|
2009-04-15 19:58:19 +00:00
|
|
|
import com.android.email.mail.Folder;
|
2009-10-07 18:42:27 +00:00
|
|
|
import com.android.email.mail.Message;
|
2009-03-04 03:32:22 +00:00
|
|
|
import com.android.email.mail.MessagingException;
|
2009-10-07 18:42:27 +00:00
|
|
|
import com.android.email.mail.Part;
|
2009-03-04 03:32:22 +00:00
|
|
|
import com.android.email.mail.Transport;
|
2009-09-25 21:54:32 +00:00
|
|
|
import com.android.email.mail.Folder.FolderType;
|
2009-03-04 03:32:22 +00:00
|
|
|
import com.android.email.mail.Folder.OpenMode;
|
2010-04-02 18:09:12 +00:00
|
|
|
import com.android.email.mail.Message.RecipientType;
|
2010-05-17 22:44:36 +00:00
|
|
|
import com.android.email.mail.internet.MimeBodyPart;
|
|
|
|
import com.android.email.mail.internet.MimeMultipart;
|
2009-10-07 18:42:27 +00:00
|
|
|
import com.android.email.mail.internet.MimeUtility;
|
2010-04-02 18:09:12 +00:00
|
|
|
import com.android.email.mail.internet.TextBody;
|
|
|
|
import com.android.email.mail.store.ImapStore.ImapMessage;
|
2009-03-04 03:32:22 +00:00
|
|
|
import com.android.email.mail.transport.MockTransport;
|
|
|
|
|
2010-05-17 22:44:36 +00:00
|
|
|
import org.apache.commons.io.IOUtils;
|
|
|
|
|
2009-03-04 03:32:22 +00:00
|
|
|
import android.test.AndroidTestCase;
|
2010-02-01 23:53:46 +00:00
|
|
|
import android.test.MoreAsserts;
|
2009-03-04 03:32:22 +00:00
|
|
|
import android.test.suitebuilder.annotation.SmallTest;
|
2009-04-15 19:58:19 +00:00
|
|
|
|
2009-10-07 18:42:27 +00:00
|
|
|
import java.util.ArrayList;
|
2010-01-26 02:40:45 +00:00
|
|
|
import java.util.HashMap;
|
2009-03-04 03:32:22 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This is a series of unit tests for the ImapStore class. These tests must be locally
|
|
|
|
* complete - no server(s) required.
|
2009-10-07 18:42:27 +00:00
|
|
|
*
|
|
|
|
* To run these tests alone, use:
|
|
|
|
* $ runtest -c com.android.email.mail.store.ImapStoreUnitTests email
|
2010-05-18 20:41:14 +00:00
|
|
|
*
|
|
|
|
* TODO Check if callback is really called
|
2009-03-04 03:32:22 +00:00
|
|
|
*/
|
|
|
|
@SmallTest
|
|
|
|
public class ImapStoreUnitTests extends AndroidTestCase {
|
2010-04-02 18:09:12 +00:00
|
|
|
private final static String[] NO_REPLY = new String[0];
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2010-05-18 20:41:14 +00:00
|
|
|
/**
|
|
|
|
* Default folder name. In order to test for encoding, we use a non-ascii name.
|
|
|
|
*/
|
|
|
|
private final static String FOLDER_NAME = "\u65E5";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Folder name encoded in UTF-7.
|
|
|
|
*/
|
|
|
|
private final static String FOLDER_ENCODED = "&ZeU-";
|
|
|
|
|
2009-03-04 03:32:22 +00:00
|
|
|
/* These values are provided by setUp() */
|
|
|
|
private ImapStore mStore = null;
|
|
|
|
private ImapStore.ImapFolder mFolder = null;
|
2010-02-26 06:48:11 +00:00
|
|
|
|
|
|
|
private int mNextTag;
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2009-03-04 03:32:22 +00:00
|
|
|
/**
|
|
|
|
* Setup code. We generate a lightweight ImapStore and ImapStore.ImapFolder.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
protected void setUp() throws Exception {
|
|
|
|
super.setUp();
|
2010-05-17 20:13:56 +00:00
|
|
|
Email.setTempDirectory(getContext());
|
|
|
|
|
2009-03-04 03:32:22 +00:00
|
|
|
// These are needed so we can get at the inner classes
|
2009-03-27 00:05:25 +00:00
|
|
|
mStore = (ImapStore) ImapStore.newInstance("imap://user:password@server:999",
|
2009-04-14 03:07:56 +00:00
|
|
|
getContext(), null);
|
2010-05-18 20:41:14 +00:00
|
|
|
mFolder = (ImapStore.ImapFolder) mStore.getFolder(FOLDER_NAME);
|
2009-03-04 03:32:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Confirms simple non-SSL non-TLS login
|
|
|
|
*/
|
|
|
|
public void testSimpleLogin() throws MessagingException {
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2009-03-04 03:32:22 +00:00
|
|
|
MockTransport mockTransport = openAndInjectMockTransport();
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2009-03-04 03:32:22 +00:00
|
|
|
// try to open it
|
|
|
|
setupOpenFolder(mockTransport);
|
AI 146134: Add persistence API for remote stores & folders to use while
syncing. This provides a key-value store, per folder, that
can be used by network Stores to record persistent data such
as sync status, server keys, etc.
Note that, by definition, this only applies to remote folders
(e.g. IMAP, POP3). You'll see everywhere that LocalFolder is
passed null, and this is correct - LocalFolder *is* persistent
storage and does not need external help.
Note to reviewers: The core changes are Folder.java,
LocalStore.java, and LocalStoreUnitTests.java, so please give
them the bulk of your reviewer attention. The other files
are just following along with minor API changes. Of those,
the one worth close examination is MessagingController.java,
which is the only place in the system where remote Folders
are bonded with Local Folders and thus where this new API
comes into play.
Note to jham: Can you please take a look at
LocalStore.LocalFolder.setPersistentString() and recommend
better SQL foo than my primitive test-then-update-or-insert
logic, which is not transactional or threadsafe.
BUG=1786939
Automated import of CL 146134
2009-04-14 17:15:07 +00:00
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2009-03-04 03:32:22 +00:00
|
|
|
// TODO: inject specific facts in the initial folder SELECT and check them here
|
|
|
|
}
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2010-01-26 02:40:45 +00:00
|
|
|
/**
|
|
|
|
* TODO: Test with SSL negotiation (faked)
|
|
|
|
* TODO: Test with SSL required but not supported
|
|
|
|
* TODO: Test with TLS negotiation (faked)
|
|
|
|
* TODO: Test with TLS required but not supported
|
|
|
|
* TODO: Test calling getMessageCount(), getMessages(), etc.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test the generation of the IMAP ID keys
|
2010-02-01 23:53:46 +00:00
|
|
|
*/
|
|
|
|
public void testImapIdBasic() {
|
|
|
|
// First test looks at operation of the outer API - we don't control any of the
|
|
|
|
// values; Just look for basic results.
|
|
|
|
|
|
|
|
// Strings we'll expect to find:
|
|
|
|
// name Android package name of the program
|
|
|
|
// os "android"
|
|
|
|
// os-version "version; build-id"
|
|
|
|
// vendor Vendor of the client/server
|
|
|
|
// x-android-device-model Model (Optional, so not tested here)
|
|
|
|
// x-android-net-operator Carrier (Unreliable, so not tested here)
|
|
|
|
// AGUID A device+account UID
|
2010-02-26 06:48:11 +00:00
|
|
|
String id = mStore.getImapId(getContext(), "user-name", "host-name", "IMAP4rev1 STARTTLS");
|
2010-02-01 23:53:46 +00:00
|
|
|
HashMap<String, String> map = tokenizeImapId(id);
|
|
|
|
assertEquals(getContext().getPackageName(), map.get("name"));
|
|
|
|
assertEquals("android", map.get("os"));
|
|
|
|
assertNotNull(map.get("os-version"));
|
|
|
|
assertNotNull(map.get("vendor"));
|
|
|
|
assertNotNull(map.get("AGUID"));
|
|
|
|
|
|
|
|
// Next, use the inner API to confirm operation of a couple of
|
|
|
|
// variants for release and non-release devices.
|
|
|
|
|
|
|
|
// simple API check - non-REL codename, non-empty version
|
|
|
|
id = mStore.makeCommonImapId("packageName", "version", "codeName",
|
|
|
|
"model", "id", "vendor", "network-operator");
|
|
|
|
map = tokenizeImapId(id);
|
|
|
|
assertEquals("packageName", map.get("name"));
|
|
|
|
assertEquals("android", map.get("os"));
|
|
|
|
assertEquals("version; id", map.get("os-version"));
|
|
|
|
assertEquals("vendor", map.get("vendor"));
|
|
|
|
assertEquals(null, map.get("x-android-device-model"));
|
|
|
|
assertEquals("network-operator", map.get("x-android-mobile-net-operator"));
|
|
|
|
assertEquals(null, map.get("AGUID"));
|
|
|
|
|
|
|
|
// simple API check - codename is REL, so use model name.
|
|
|
|
// also test empty version => 1.0 and empty network operator
|
|
|
|
id = mStore.makeCommonImapId("packageName", "", "REL",
|
|
|
|
"model", "id", "vendor", "");
|
|
|
|
map = tokenizeImapId(id);
|
|
|
|
assertEquals("packageName", map.get("name"));
|
|
|
|
assertEquals("android", map.get("os"));
|
|
|
|
assertEquals("1.0; id", map.get("os-version"));
|
|
|
|
assertEquals("vendor", map.get("vendor"));
|
|
|
|
assertEquals("model", map.get("x-android-device-model"));
|
|
|
|
assertEquals(null, map.get("x-android-mobile-net-operator"));
|
|
|
|
assertEquals(null, map.get("AGUID"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test of the internal generator for IMAP ID strings, specifically looking for proper
|
|
|
|
* filtering of illegal values. This is required because we cannot necessarily trust
|
|
|
|
* the external sources of some of this data (e.g. release labels).
|
2010-01-26 02:40:45 +00:00
|
|
|
*
|
2010-02-01 23:53:46 +00:00
|
|
|
* The (somewhat arbitrary) legal values are: a-z A-Z 0-9 - _ + = ; : . , / <space>
|
|
|
|
* The most important goal of the filters is to keep out control chars, (, ), and "
|
|
|
|
*/
|
|
|
|
public void testImapIdFiltering() {
|
|
|
|
String id = mStore.makeCommonImapId("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
|
|
"0123456789", "codeName",
|
|
|
|
"model", "-_+=;:.,// ",
|
|
|
|
"v(e)n\"d\ro\nr", // look for bad chars stripped out, leaving OK chars
|
|
|
|
"()\""); // look for bad chars stripped out, leaving nothing
|
|
|
|
HashMap<String, String> map = tokenizeImapId(id);
|
|
|
|
|
|
|
|
assertEquals("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", map.get("name"));
|
|
|
|
assertEquals("0123456789; -_+=;:.,// ", map.get("os-version"));
|
|
|
|
assertEquals("vendor", map.get("vendor"));
|
|
|
|
assertNull(map.get("x-android-mobile-net-operator"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that IMAP ID uid's are per-username
|
2010-01-26 02:40:45 +00:00
|
|
|
*/
|
2010-02-01 23:53:46 +00:00
|
|
|
public void testImapIdDeviceId() throws MessagingException {
|
|
|
|
ImapStore store1a = (ImapStore) ImapStore.newInstance("imap://user1:password@server:999",
|
|
|
|
getContext(), null);
|
|
|
|
ImapStore store1b = (ImapStore) ImapStore.newInstance("imap://user1:password@server:999",
|
|
|
|
getContext(), null);
|
|
|
|
ImapStore store2 = (ImapStore) ImapStore.newInstance("imap://user2:password@server:999",
|
|
|
|
getContext(), null);
|
|
|
|
|
2010-02-26 06:48:11 +00:00
|
|
|
String id1a = mStore.getImapId(getContext(), "user1", "host-name", "IMAP4rev1");
|
|
|
|
String id1b = mStore.getImapId(getContext(), "user1", "host-name", "IMAP4rev1");
|
|
|
|
String id2 = mStore.getImapId(getContext(), "user2", "host-name", "IMAP4rev1");
|
2010-02-01 23:53:46 +00:00
|
|
|
|
|
|
|
String uid1a = tokenizeImapId(id1a).get("AGUID");
|
|
|
|
String uid1b = tokenizeImapId(id1b).get("AGUID");
|
|
|
|
String uid2 = tokenizeImapId(id2).get("AGUID");
|
|
|
|
|
|
|
|
assertEquals(uid1a, uid1b);
|
|
|
|
MoreAsserts.assertNotEqual(uid1a, uid2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper to break an IMAP ID string into keys & values
|
|
|
|
* @param id the IMAP Id string (the part inside the parens)
|
|
|
|
* @return a map of key/value pairs
|
|
|
|
*/
|
|
|
|
private HashMap<String, String> tokenizeImapId(String id) {
|
2010-01-26 02:40:45 +00:00
|
|
|
// Instead of a true tokenizer, we'll use double-quote as the split.
|
2010-05-17 20:13:56 +00:00
|
|
|
// We can's use " " because there may be spaces inside the values.
|
2010-01-26 02:40:45 +00:00
|
|
|
String[] elements = id.split("\"");
|
|
|
|
HashMap<String, String> map = new HashMap<String, String>();
|
|
|
|
for (int i = 0; i < elements.length; ) {
|
|
|
|
// Because we split at quotes, we expect to find:
|
2010-02-01 23:53:46 +00:00
|
|
|
// [i] = null or one or more spaces
|
2010-01-26 02:40:45 +00:00
|
|
|
// [i+1] = key
|
|
|
|
// [i+2] = one or more spaces
|
|
|
|
// [i+3] = value
|
|
|
|
map.put(elements[i+1], elements[i+3]);
|
|
|
|
i += 4;
|
|
|
|
}
|
2010-02-01 23:53:46 +00:00
|
|
|
return map;
|
2010-01-26 02:40:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test non-NIL server response to IMAP ID. We should simply ignore it.
|
|
|
|
*/
|
|
|
|
public void testServerId() throws MessagingException {
|
|
|
|
MockTransport mockTransport = openAndInjectMockTransport();
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2010-01-26 02:40:45 +00:00
|
|
|
// try to open it
|
|
|
|
setupOpenFolder(mockTransport, new String[] {
|
|
|
|
"* ID (\"name\" \"Cyrus\" \"version\" \"1.5\"" +
|
|
|
|
" \"os\" \"sunos\" \"os-version\" \"5.5\"" +
|
|
|
|
" \"support-url\" \"mailto:cyrus-bugs+@andrew.cmu.edu\")",
|
2010-03-18 17:11:08 +00:00
|
|
|
"OK"}, "READ-WRITE");
|
2010-01-26 02:40:45 +00:00
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test OK response to IMAP ID with crummy text afterwards too.
|
|
|
|
*/
|
|
|
|
public void testImapIdOkParsing() throws MessagingException {
|
|
|
|
MockTransport mockTransport = openAndInjectMockTransport();
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2010-01-26 02:40:45 +00:00
|
|
|
// try to open it
|
|
|
|
setupOpenFolder(mockTransport, new String[] {
|
|
|
|
"* ID NIL",
|
2010-03-18 17:11:08 +00:00
|
|
|
"OK [ID] bad-char-%"}, "READ-WRITE");
|
2010-01-26 02:40:45 +00:00
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
}
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2010-01-26 02:40:45 +00:00
|
|
|
/**
|
|
|
|
* Test BAD response to IMAP ID - also with bad parser chars
|
|
|
|
*/
|
|
|
|
public void testImapIdBad() throws MessagingException {
|
|
|
|
MockTransport mockTransport = openAndInjectMockTransport();
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2010-01-26 02:40:45 +00:00
|
|
|
// try to open it
|
|
|
|
setupOpenFolder(mockTransport, new String[] {
|
2010-03-18 17:11:08 +00:00
|
|
|
"BAD unknown command bad-char-%"}, "READ-WRITE");
|
2010-01-26 02:40:45 +00:00
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
}
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2009-03-04 03:32:22 +00:00
|
|
|
/**
|
|
|
|
* TODO: Test the operation of checkSettings()
|
|
|
|
* TODO: Test small Store & Folder functions that manage folders & namespace
|
2010-05-17 20:13:56 +00:00
|
|
|
*/
|
2009-05-20 17:36:16 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Test small Folder functions that don't really do anything in Imap
|
|
|
|
* TODO: Test all of the small Folder functions.
|
|
|
|
*/
|
|
|
|
public void testSmallFolderFunctions() throws MessagingException {
|
2009-08-27 06:12:02 +00:00
|
|
|
// getPermanentFlags() returns { Flag.DELETED, Flag.SEEN, Flag.FLAGGED }
|
2009-05-20 17:36:16 +00:00
|
|
|
Flag[] flags = mFolder.getPermanentFlags();
|
2009-08-27 06:12:02 +00:00
|
|
|
assertEquals(3, flags.length);
|
2009-05-20 17:36:16 +00:00
|
|
|
// TODO: Write flags into hashset and compare them to a hashset and compare them
|
|
|
|
assertEquals(Flag.DELETED, flags[0]);
|
|
|
|
assertEquals(Flag.SEEN, flags[1]);
|
2009-08-27 06:12:02 +00:00
|
|
|
assertEquals(Flag.FLAGGED, flags[2]);
|
2009-09-25 21:54:32 +00:00
|
|
|
|
|
|
|
// canCreate() returns true
|
|
|
|
assertTrue(mFolder.canCreate(FolderType.HOLDS_FOLDERS));
|
|
|
|
assertTrue(mFolder.canCreate(FolderType.HOLDS_MESSAGES));
|
2009-05-20 17:36:16 +00:00
|
|
|
}
|
|
|
|
|
2009-04-15 19:58:19 +00:00
|
|
|
/**
|
|
|
|
* Lightweight test to confirm that IMAP hasn't implemented any folder roles yet.
|
2010-05-17 20:13:56 +00:00
|
|
|
*
|
2009-04-15 19:58:19 +00:00
|
|
|
* TODO: Test this with multiple folders provided by mock server
|
2009-05-01 23:36:34 +00:00
|
|
|
* TODO: Implement XLIST and then support this
|
2009-04-15 19:58:19 +00:00
|
|
|
*/
|
|
|
|
public void testNoFolderRolesYet() {
|
2010-05-17 20:13:56 +00:00
|
|
|
assertEquals(Folder.FolderRole.UNKNOWN, mFolder.getRole());
|
2009-04-15 19:58:19 +00:00
|
|
|
}
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2009-05-01 23:36:34 +00:00
|
|
|
/**
|
|
|
|
* Lightweight test to confirm that IMAP isn't requesting structure prefetch.
|
|
|
|
*/
|
|
|
|
public void testNoStructurePrefetch() {
|
2010-05-17 20:13:56 +00:00
|
|
|
assertFalse(mStore.requireStructurePrefetch());
|
2009-05-01 23:36:34 +00:00
|
|
|
}
|
2010-05-17 20:13:56 +00:00
|
|
|
|
2009-05-01 23:36:34 +00:00
|
|
|
/**
|
|
|
|
* Lightweight test to confirm that IMAP is requesting sent-message-upload.
|
|
|
|
* TODO: Implement Gmail-specific cases and handle this server-side
|
|
|
|
*/
|
|
|
|
public void testSentUploadRequested() {
|
2010-05-17 20:13:56 +00:00
|
|
|
assertTrue(mStore.requireCopyMessageToSentFolder());
|
2009-05-01 23:36:34 +00:00
|
|
|
}
|
|
|
|
|
2009-03-04 03:32:22 +00:00
|
|
|
/**
|
|
|
|
* TODO: Test the process of opening and indexing a mailbox with one unread message in it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2010-05-17 20:13:56 +00:00
|
|
|
* TODO: Test the scenario where the transport is "open" but not really (e.g. server closed).
|
2009-03-04 03:32:22 +00:00
|
|
|
/**
|
|
|
|
* Set up a basic MockTransport. open it, and inject it into mStore
|
|
|
|
*/
|
|
|
|
private MockTransport openAndInjectMockTransport() {
|
|
|
|
// Create mock transport and inject it into the ImapStore that's already set up
|
|
|
|
MockTransport mockTransport = new MockTransport();
|
2009-09-29 22:28:43 +00:00
|
|
|
mockTransport.setSecurity(Transport.CONNECTION_SECURITY_NONE, false);
|
2010-02-26 06:48:11 +00:00
|
|
|
mockTransport.setMockHost("mock.server.com");
|
2009-03-04 03:32:22 +00:00
|
|
|
mStore.setTransport(mockTransport);
|
|
|
|
return mockTransport;
|
|
|
|
}
|
2010-03-18 17:11:08 +00:00
|
|
|
|
2009-03-04 03:32:22 +00:00
|
|
|
/**
|
|
|
|
* Helper which stuffs the mock with enough strings to satisfy a call to ImapFolder.open()
|
2010-03-18 17:11:08 +00:00
|
|
|
*
|
2009-03-04 03:32:22 +00:00
|
|
|
* @param mockTransport the mock transport we're using
|
|
|
|
*/
|
|
|
|
private void setupOpenFolder(MockTransport mockTransport) {
|
2010-03-18 17:11:08 +00:00
|
|
|
setupOpenFolder(mockTransport, "READ-WRITE");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper which stuffs the mock with enough strings to satisfy a call to ImapFolder.open()
|
|
|
|
*
|
|
|
|
* @param mockTransport the mock transport we're using
|
|
|
|
*/
|
|
|
|
private void setupOpenFolder(MockTransport mockTransport, String readWriteMode) {
|
2010-01-26 02:40:45 +00:00
|
|
|
setupOpenFolder(mockTransport, new String[] {
|
2010-03-18 17:11:08 +00:00
|
|
|
"* ID NIL", "OK"}, readWriteMode);
|
2010-01-26 02:40:45 +00:00
|
|
|
}
|
2010-03-18 17:11:08 +00:00
|
|
|
|
2010-01-26 02:40:45 +00:00
|
|
|
/**
|
|
|
|
* Helper which stuffs the mock with enough strings to satisfy a call to ImapFolder.open()
|
|
|
|
* Also allows setting a custom IMAP ID.
|
2010-02-26 06:48:11 +00:00
|
|
|
*
|
|
|
|
* Also sets mNextTag, an int, which is useful if there are additional commands to inject.
|
2010-03-18 17:11:08 +00:00
|
|
|
*
|
2010-01-26 02:40:45 +00:00
|
|
|
* @param mockTransport the mock transport we're using
|
2010-02-26 06:48:11 +00:00
|
|
|
* @param imapIdResponse the expected series of responses to the IMAP ID command. Non-final
|
|
|
|
* lines should be tagged with *. The final response should be untagged (the correct
|
|
|
|
* tag will be added at runtime).
|
2010-03-18 17:11:08 +00:00
|
|
|
* @param "READ-WRITE" or "READ-ONLY"
|
2010-02-26 06:48:11 +00:00
|
|
|
* @return the next tag# to use
|
2010-01-26 02:40:45 +00:00
|
|
|
*/
|
2010-03-18 17:11:08 +00:00
|
|
|
private void setupOpenFolder(MockTransport mockTransport, String[] imapIdResponse,
|
|
|
|
String readWriteMode) {
|
2010-05-18 20:41:14 +00:00
|
|
|
expectLogin(mockTransport, imapIdResponse);
|
|
|
|
mockTransport.expect(
|
|
|
|
getNextTag(false) + " SELECT \"" + FOLDER_ENCODED + "\"", new String[] {
|
|
|
|
"* FLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen)",
|
|
|
|
"* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen \\*)]",
|
|
|
|
"* 0 EXISTS",
|
|
|
|
"* 0 RECENT",
|
|
|
|
"* OK [UNSEEN 0]",
|
|
|
|
"* OK [UIDNEXT 1]",
|
|
|
|
getNextTag(true) + " OK [" + readWriteMode + "] " +
|
|
|
|
FOLDER_ENCODED + " selected. (Success)"});
|
|
|
|
}
|
|
|
|
|
|
|
|
private void expectLogin(MockTransport mockTransport, String[] imapIdResponse) {
|
2010-02-26 06:48:11 +00:00
|
|
|
// Fix the tag # of the ID response
|
|
|
|
String last = imapIdResponse[imapIdResponse.length-1];
|
|
|
|
last = "2 " + last;
|
|
|
|
imapIdResponse[imapIdResponse.length-1] = last;
|
|
|
|
// inject boilerplate commands that match our typical login
|
2009-03-04 03:32:22 +00:00
|
|
|
mockTransport.expect(null, "* OK Imap 2000 Ready To Assist You");
|
2010-02-26 06:48:11 +00:00
|
|
|
mockTransport.expect("1 CAPABILITY", new String[] {
|
|
|
|
"* CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED",
|
|
|
|
"1 OK CAPABILITY completed"});
|
|
|
|
mockTransport.expect("2 ID \\(.*\\)", imapIdResponse);
|
2010-05-17 20:13:56 +00:00
|
|
|
mockTransport.expect("3 LOGIN user \"password\"",
|
2010-02-26 06:48:11 +00:00
|
|
|
"3 OK user authenticated (Success)");
|
2010-05-18 20:41:14 +00:00
|
|
|
mNextTag = 4;
|
2010-02-26 06:48:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a tag for use in setting up expect strings. Typically this is called in pairs,
|
|
|
|
* first as getNextTag(false) when emitting the command, then as getNextTag(true) when
|
|
|
|
* emitting the final line of the expected response.
|
|
|
|
* @param advance true to increment mNextTag for the subsequence command
|
|
|
|
* @return a string containing the current tag
|
|
|
|
*/
|
|
|
|
public String getNextTag(boolean advance) {
|
|
|
|
if (advance) ++mNextTag;
|
|
|
|
return Integer.toString(mNextTag);
|
2009-03-04 03:32:22 +00:00
|
|
|
}
|
2010-03-18 17:11:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that servers reporting READ-WRITE mode are parsed properly
|
|
|
|
* Note: the READ_WRITE mode passed to folder.open() does not affect the test
|
|
|
|
*/
|
|
|
|
public void testReadWrite() throws MessagingException {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock, "READ-WRITE");
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
assertEquals(OpenMode.READ_WRITE, mFolder.getMode());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that servers reporting READ-ONLY mode are parsed properly
|
|
|
|
* Note: the READ_ONLY mode passed to folder.open() does not affect the test
|
|
|
|
*/
|
|
|
|
public void testReadOnly() throws MessagingException {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock, "READ-ONLY");
|
|
|
|
mFolder.open(OpenMode.READ_ONLY, null);
|
|
|
|
assertEquals(OpenMode.READ_ONLY, mFolder.getMode());
|
|
|
|
}
|
|
|
|
|
2009-05-19 21:54:49 +00:00
|
|
|
/**
|
|
|
|
* Test for getUnreadMessageCount with quoted string in the middle of response.
|
|
|
|
*/
|
|
|
|
public void testGetUnreadMessageCountWithQuotedString() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
2010-05-18 20:41:14 +00:00
|
|
|
mock.expect(
|
|
|
|
getNextTag(false) + " STATUS \"" + FOLDER_ENCODED + "\" \\(UNSEEN\\)",
|
|
|
|
new String[] {
|
|
|
|
"* STATUS \"" + FOLDER_ENCODED + "\" (UNSEEN 2)",
|
2010-02-26 06:48:11 +00:00
|
|
|
getNextTag(true) + " OK STATUS completed"});
|
2009-05-19 21:54:49 +00:00
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
int unreadCount = mFolder.getUnreadMessageCount();
|
|
|
|
assertEquals("getUnreadMessageCount with quoted string", 2, unreadCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test for getUnreadMessageCount with literal string in the middle of response.
|
|
|
|
*/
|
|
|
|
public void testGetUnreadMessageCountWithLiteralString() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
2010-05-18 20:41:14 +00:00
|
|
|
mock.expect(
|
|
|
|
getNextTag(false) + " STATUS \"" + FOLDER_ENCODED + "\" \\(UNSEEN\\)",
|
|
|
|
new String[] {
|
2009-05-19 21:54:49 +00:00
|
|
|
"* STATUS {5}",
|
2010-05-18 20:41:14 +00:00
|
|
|
FOLDER_ENCODED + " (UNSEEN 10)",
|
2010-02-26 06:48:11 +00:00
|
|
|
getNextTag(true) + " OK STATUS completed"});
|
2009-05-19 21:54:49 +00:00
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
int unreadCount = mFolder.getUnreadMessageCount();
|
|
|
|
assertEquals("getUnreadMessageCount with literal string", 10, unreadCount);
|
|
|
|
}
|
2009-10-07 18:42:27 +00:00
|
|
|
|
2010-05-17 22:44:36 +00:00
|
|
|
public void testFetchFlagEnvelope() throws MessagingException {
|
|
|
|
final MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
final Message message = mFolder.createMessage("1");
|
|
|
|
|
|
|
|
final FetchProfile fp = new FetchProfile();
|
|
|
|
fp.add(FetchProfile.Item.FLAGS);
|
|
|
|
fp.add(FetchProfile.Item.ENVELOPE);
|
|
|
|
mock.expect(getNextTag(false) +
|
|
|
|
" UID FETCH 1 \\(UID FLAGS INTERNALDATE RFC822\\.SIZE BODY\\.PEEK\\[HEADER.FIELDS" +
|
|
|
|
" \\(date subject from content-type to cc message-id\\)\\]\\)",
|
|
|
|
new String[] {
|
|
|
|
"* 9 FETCH (UID 1 RFC822.SIZE 120626 INTERNALDATE \"17-May-2010 22:00:15 +0000\"" +
|
|
|
|
"FLAGS (\\Seen) BODY[HEADER.FIELDS (date subject from content-type to cc" +
|
|
|
|
" message-id)]" +
|
|
|
|
" {279}",
|
|
|
|
"From: Xxxxxx Yyyyy <userxx@android.com>",
|
|
|
|
"Date: Mon, 17 May 2010 14:59:52 -0700",
|
|
|
|
"Message-ID: <x0000000000000000000000000000000000000000000000y@android.com>",
|
|
|
|
"Subject: ssubject",
|
|
|
|
"To: android.test01@android.com",
|
|
|
|
"Content-Type: multipart/mixed; boundary=a00000000000000000000000000b",
|
|
|
|
"",
|
|
|
|
")",
|
|
|
|
getNextTag(true) + " OK SUCCESS"
|
|
|
|
});
|
|
|
|
mFolder.fetch(new Message[] { message }, fp, null);
|
|
|
|
|
|
|
|
assertEquals("android.test01@android.com", message.getHeader("to")[0]);
|
|
|
|
assertEquals("Xxxxxx Yyyyy <userxx@android.com>", message.getHeader("from")[0]);
|
|
|
|
assertEquals("multipart/mixed; boundary=a00000000000000000000000000b",
|
|
|
|
message.getHeader("Content-Type")[0]);
|
|
|
|
assertTrue(message.isSet(Flag.SEEN));
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testFetchBodyStructure() throws MessagingException {
|
|
|
|
final MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
final Message message = mFolder.createMessage("1");
|
|
|
|
|
|
|
|
final FetchProfile fp = new FetchProfile();
|
|
|
|
fp.add(FetchProfile.Item.STRUCTURE);
|
|
|
|
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODYSTRUCTURE\\)",
|
|
|
|
new String[] {
|
|
|
|
"* 9 FETCH (UID 1 BODYSTRUCTURE ((\"TEXT\" \"PLAIN\" (\"CHARSET\" \"ISO-8859-1\")" +
|
|
|
|
" CID NIL \"7BIT\" 18 3 NIL NIL NIL)" +
|
|
|
|
"(\"IMAGE\" \"PNG\"" +
|
|
|
|
" (\"NAME\" \"device.png\") NIL NIL \"BASE64\" 117840 NIL (\"ATTACHMENT\"" +
|
|
|
|
"(\"FILENAME\" \"device.png\")) NIL)" +
|
|
|
|
"(\"TEXT\" \"HTML\"" +
|
|
|
|
" () NIL NIL \"7BIT\" 100 NIL NIL (\"ATTACHMENT\"" +
|
|
|
|
"(\"FILENAME\" \"attachment.html\" \"SIZE\" 555)) NIL)" +
|
|
|
|
"\"MIXED\" (\"BOUNDARY\" \"00032556278a7005e40486d159ca\") NIL NIL))",
|
|
|
|
getNextTag(true) + " OK SUCCESS"
|
|
|
|
});
|
|
|
|
mFolder.fetch(new Message[] { message }, fp, null);
|
|
|
|
|
|
|
|
// Check mime structure...
|
|
|
|
Body body = message.getBody();
|
|
|
|
assertTrue(body instanceof MimeMultipart);
|
|
|
|
MimeMultipart mimeMultipart = (MimeMultipart) body;
|
|
|
|
assertEquals(3, mimeMultipart.getCount());
|
|
|
|
assertEquals("mixed", mimeMultipart.getSubTypeForTest());
|
|
|
|
|
|
|
|
Part part0 = mimeMultipart.getBodyPart(0);
|
|
|
|
Part part1 = mimeMultipart.getBodyPart(1);
|
|
|
|
Part part2 = mimeMultipart.getBodyPart(2);
|
|
|
|
assertTrue(part0 instanceof MimeBodyPart);
|
|
|
|
assertTrue(part1 instanceof MimeBodyPart);
|
|
|
|
assertTrue(part2 instanceof MimeBodyPart);
|
|
|
|
|
|
|
|
MimeBodyPart mimePart0 = (MimeBodyPart) part0; // text/plain
|
|
|
|
MimeBodyPart mimePart1 = (MimeBodyPart) part1; // image/png
|
|
|
|
MimeBodyPart mimePart2 = (MimeBodyPart) part2; // text/html
|
|
|
|
|
|
|
|
MoreAsserts.assertEquals(
|
|
|
|
new String[] {"text/plain;\n CHARSET=\"ISO-8859-1\""},
|
|
|
|
part0.getHeader("Content-Type")
|
|
|
|
);
|
|
|
|
MoreAsserts.assertEquals(
|
|
|
|
new String[] {"image/png;\n NAME=\"device.png\""},
|
|
|
|
part1.getHeader("Content-Type")
|
|
|
|
);
|
|
|
|
MoreAsserts.assertEquals(
|
|
|
|
new String[] {"text/html"},
|
|
|
|
part2.getHeader("Content-Type")
|
|
|
|
);
|
|
|
|
|
|
|
|
MoreAsserts.assertEquals(
|
|
|
|
new String[] {"CID"},
|
|
|
|
part0.getHeader("Content-ID")
|
|
|
|
);
|
|
|
|
assertNull(
|
|
|
|
part1.getHeader("Content-ID")
|
|
|
|
);
|
|
|
|
assertNull(
|
|
|
|
part2.getHeader("Content-ID")
|
|
|
|
);
|
|
|
|
|
|
|
|
MoreAsserts.assertEquals(
|
|
|
|
new String[] {"7BIT"},
|
|
|
|
part0.getHeader("Content-Transfer-Encoding")
|
|
|
|
);
|
|
|
|
MoreAsserts.assertEquals(
|
|
|
|
new String[] {"BASE64"},
|
|
|
|
part1.getHeader("Content-Transfer-Encoding")
|
|
|
|
);
|
|
|
|
MoreAsserts.assertEquals(
|
|
|
|
new String[] {"7BIT"},
|
|
|
|
part2.getHeader("Content-Transfer-Encoding")
|
|
|
|
);
|
|
|
|
|
|
|
|
MoreAsserts.assertEquals(
|
|
|
|
new String[] {";\n size=18"}, // TODO Is that right?
|
|
|
|
part0.getHeader("Content-Disposition")
|
|
|
|
);
|
|
|
|
MoreAsserts.assertEquals(
|
|
|
|
new String[] {"attachment;\n filename=\"device.png\";\n size=117840"},
|
|
|
|
part1.getHeader("Content-Disposition")
|
|
|
|
);
|
|
|
|
MoreAsserts.assertEquals(
|
|
|
|
new String[] {"attachment;\n filename=\"attachment.html\";\n size=\"555\""},
|
|
|
|
part2.getHeader("Content-Disposition")
|
|
|
|
);
|
|
|
|
|
|
|
|
// TODO Test for quote: If a filename contains ", it should become %22,
|
|
|
|
// which isn't implemented.
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testFetchBodySane() throws MessagingException {
|
|
|
|
final MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
final Message message = mFolder.createMessage("1");
|
|
|
|
|
|
|
|
final FetchProfile fp = new FetchProfile();
|
|
|
|
fp.add(FetchProfile.Item.BODY_SANE);
|
|
|
|
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODY.PEEK\\[\\]<0.51200>\\)",
|
|
|
|
new String[] {
|
|
|
|
"* 9 FETCH (UID 1 BODY[] {23}",
|
|
|
|
"from: a@b.com", // 15 bytes
|
|
|
|
"", // 2
|
|
|
|
"test", // 6
|
|
|
|
")",
|
|
|
|
getNextTag(true) + " OK SUCCESS"
|
|
|
|
});
|
|
|
|
mFolder.fetch(new Message[] { message }, fp, null);
|
|
|
|
assertEquals("a@b.com", message.getHeader("from")[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testFetchBody() throws MessagingException {
|
|
|
|
final MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
final Message message = mFolder.createMessage("1");
|
|
|
|
|
|
|
|
final FetchProfile fp = new FetchProfile();
|
|
|
|
fp.add(FetchProfile.Item.BODY);
|
|
|
|
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODY.PEEK\\[\\]\\)",
|
|
|
|
new String[] {
|
|
|
|
"* 9 FETCH (UID 1 BODY[] {23}",
|
|
|
|
"from: a@b.com", // 15 bytes
|
|
|
|
"", // 2
|
|
|
|
"test", // 6
|
|
|
|
")",
|
|
|
|
getNextTag(true) + " OK SUCCESS"
|
|
|
|
});
|
|
|
|
mFolder.fetch(new Message[] { message }, fp, null);
|
|
|
|
assertEquals("a@b.com", message.getHeader("from")[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testFetchAttachment() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
final Message message = mFolder.createMessage("1");
|
|
|
|
|
|
|
|
final FetchProfile fp = new FetchProfile();
|
|
|
|
fp.add(FetchProfile.Item.STRUCTURE);
|
|
|
|
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODYSTRUCTURE\\)",
|
|
|
|
new String[] {
|
|
|
|
"* 9 FETCH (UID 1 BODYSTRUCTURE ((\"TEXT\" \"PLAIN\" (\"CHARSET\" \"ISO-8859-1\")" +
|
|
|
|
" CID NIL \"7BIT\" 18 3 NIL NIL NIL)" +
|
|
|
|
"(\"IMAGE\" \"PNG\"" +
|
|
|
|
" (\"NAME\" \"device.png\") NIL NIL \"BASE64\" 117840 NIL (\"ATTACHMENT\"" +
|
|
|
|
"(\"FILENAME\" \"device.png\")) NIL)" +
|
|
|
|
"\"MIXED\"))",
|
|
|
|
getNextTag(true) + " OK SUCCESS"
|
|
|
|
});
|
|
|
|
mFolder.fetch(new Message[] { message }, fp, null);
|
|
|
|
|
|
|
|
// Check mime structure, and get the second part.
|
|
|
|
Body body = message.getBody();
|
|
|
|
assertTrue(body instanceof MimeMultipart);
|
|
|
|
MimeMultipart mimeMultipart = (MimeMultipart) body;
|
|
|
|
assertEquals(2, mimeMultipart.getCount());
|
|
|
|
|
|
|
|
Part part1 = mimeMultipart.getBodyPart(1);
|
|
|
|
assertTrue(part1 instanceof MimeBodyPart);
|
|
|
|
MimeBodyPart mimePart1 = (MimeBodyPart) part1;
|
|
|
|
|
|
|
|
// Fetch the second part
|
|
|
|
fp.clear();
|
|
|
|
fp.add(mimePart1);
|
|
|
|
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODY.PEEK\\[2\\]\\)",
|
|
|
|
new String[] {
|
|
|
|
"* 9 FETCH (UID 1 BODY[2] {4}",
|
|
|
|
"YWJj)", // abc in base64
|
|
|
|
getNextTag(true) + " OK SUCCESS"
|
|
|
|
});
|
|
|
|
mFolder.fetch(new Message[] { message }, fp, null);
|
|
|
|
|
|
|
|
assertEquals("abc",
|
|
|
|
Utility.fromUtf8(IOUtils.toByteArray(mimePart1.getBody().getInputStream())));
|
|
|
|
}
|
|
|
|
|
2009-10-07 18:42:27 +00:00
|
|
|
/**
|
|
|
|
* Test for proper operations on servers that return "NIL" for empty message bodies.
|
|
|
|
*/
|
|
|
|
public void testNilMessage() throws MessagingException {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
|
|
|
|
// Prepare to pull structure and peek body text - this is like the "large message"
|
|
|
|
// loop in MessagingController.synchronizeMailboxGeneric()
|
|
|
|
FetchProfile fp = new FetchProfile();fp.clear();
|
|
|
|
fp.add(FetchProfile.Item.STRUCTURE);
|
|
|
|
Message message1 = mFolder.createMessage("1");
|
2010-02-26 06:48:11 +00:00
|
|
|
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODYSTRUCTURE\\)", new String[] {
|
2009-10-07 18:42:27 +00:00
|
|
|
"* 1 FETCH (UID 1 BODYSTRUCTURE (TEXT PLAIN NIL NIL NIL 7BIT 0 0 NIL NIL NIL))",
|
2010-02-26 06:48:11 +00:00
|
|
|
getNextTag(true) + " OK SUCCESS"
|
2009-10-07 18:42:27 +00:00
|
|
|
});
|
|
|
|
mFolder.fetch(new Message[] { message1 }, fp, null);
|
|
|
|
|
|
|
|
// The expected result for an empty body is:
|
|
|
|
// * 1 FETCH (UID 1 BODY[TEXT] {0})
|
|
|
|
// But some servers are returning NIL for the empty body:
|
|
|
|
// * 1 FETCH (UID 1 BODY[TEXT] NIL)
|
|
|
|
// Because this breaks our little parser, fetch() skips over empty parts.
|
|
|
|
// The rest of this test is confirming that this is the case.
|
|
|
|
|
2010-02-26 06:48:11 +00:00
|
|
|
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODY.PEEK\\[TEXT\\]\\)", new String[] {
|
2009-10-07 18:42:27 +00:00
|
|
|
"* 1 FETCH (UID 1 BODY[TEXT] NIL)",
|
2010-02-26 06:48:11 +00:00
|
|
|
getNextTag(true) + " OK SUCCESS"
|
2009-10-07 18:42:27 +00:00
|
|
|
});
|
|
|
|
ArrayList<Part> viewables = new ArrayList<Part>();
|
|
|
|
ArrayList<Part> attachments = new ArrayList<Part>();
|
|
|
|
MimeUtility.collectParts(message1, viewables, attachments);
|
|
|
|
assertTrue(viewables.size() == 1);
|
|
|
|
Part emptyBodyPart = viewables.get(0);
|
|
|
|
fp.clear();
|
|
|
|
fp.add(emptyBodyPart);
|
|
|
|
mFolder.fetch(new Message[] { message1 }, fp, null);
|
|
|
|
|
|
|
|
// If this wasn't working properly, there would be an attempted interpretation
|
|
|
|
// of the empty part's NIL and possibly a crash.
|
|
|
|
|
|
|
|
// If this worked properly, the "empty" body can now be retrieved
|
|
|
|
viewables = new ArrayList<Part>();
|
|
|
|
attachments = new ArrayList<Part>();
|
|
|
|
MimeUtility.collectParts(message1, viewables, attachments);
|
|
|
|
assertTrue(viewables.size() == 1);
|
|
|
|
emptyBodyPart = viewables.get(0);
|
|
|
|
String text = MimeUtility.getTextFromPart(emptyBodyPart);
|
|
|
|
assertNull(text);
|
|
|
|
}
|
2010-02-24 01:29:24 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Confirm the IMAP parser won't crash when seeing an excess FETCH response line without UID.
|
|
|
|
*
|
|
|
|
* <p>We've observed that the secure.emailsrvr.com email server returns an excess FETCH response
|
|
|
|
* for a UID FETCH command. These excess responses doesn't have the UID field in it, even
|
|
|
|
* though we request, which led the response parser to crash. We fixed it by ignoring response
|
|
|
|
* lines that don't have UID. This test is to make sure this case.
|
|
|
|
*/
|
|
|
|
public void testExcessFetchResult() throws MessagingException {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
|
|
|
|
// Create a message, and make sure it's not "SEEN".
|
|
|
|
Message message1 = mFolder.createMessage("1");
|
|
|
|
assertFalse(message1.isSet(Flag.SEEN));
|
|
|
|
|
|
|
|
FetchProfile fp = new FetchProfile();
|
|
|
|
fp.clear();
|
|
|
|
fp.add(FetchProfile.Item.FLAGS);
|
|
|
|
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID FLAGS\\)",
|
|
|
|
new String[] {
|
|
|
|
"* 1 FETCH (UID 1 FLAGS (\\Seen))",
|
|
|
|
"* 2 FETCH (FLAGS (\\Seen))",
|
|
|
|
getNextTag(true) + " OK SUCCESS"
|
|
|
|
});
|
|
|
|
|
|
|
|
// Shouldn't crash
|
|
|
|
mFolder.fetch(new Message[] { message1 }, fp, null);
|
|
|
|
|
|
|
|
// And the message is "SEEN".
|
|
|
|
assertTrue(message1.isSet(Flag.SEEN));
|
|
|
|
}
|
2010-04-02 18:09:12 +00:00
|
|
|
|
|
|
|
public void testAppendMessages() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
|
|
|
|
ImapMessage message = (ImapMessage) mFolder.createMessage("1");
|
|
|
|
message.setFrom(new Address("me@test.com"));
|
|
|
|
message.setRecipient(RecipientType.TO, new Address("you@test.com"));
|
|
|
|
message.setMessageId("<message.id@test.com>");
|
|
|
|
message.setFlagDirectlyForTest(Flag.SEEN, true);
|
|
|
|
message.setBody(new TextBody("Test Body"));
|
|
|
|
|
|
|
|
// + go ahead
|
|
|
|
// * 12345 EXISTS
|
|
|
|
// OK [APPENDUID 627684530 17] (Success)
|
|
|
|
|
2010-05-18 20:41:14 +00:00
|
|
|
mock.expect(getNextTag(false) +
|
|
|
|
" APPEND \\\"" + FOLDER_ENCODED + "\\\" \\(\\\\Seen\\) \\{166\\}",
|
2010-04-02 18:09:12 +00:00
|
|
|
new String[] {"+ go ahead"});
|
|
|
|
|
|
|
|
mock.expectLiterally("From: me@test.com", NO_REPLY);
|
|
|
|
mock.expectLiterally("To: you@test.com", NO_REPLY);
|
|
|
|
mock.expectLiterally("Message-ID: <message.id@test.com>", NO_REPLY);
|
|
|
|
mock.expectLiterally("Content-Type: text/plain;", NO_REPLY);
|
|
|
|
mock.expectLiterally(" charset=utf-8", NO_REPLY);
|
|
|
|
mock.expectLiterally("Content-Transfer-Encoding: base64", NO_REPLY);
|
|
|
|
mock.expectLiterally("", NO_REPLY);
|
|
|
|
mock.expectLiterally("VGVzdCBCb2R5", NO_REPLY);
|
|
|
|
mock.expectLiterally("", new String[] {
|
|
|
|
"* 7 EXISTS",
|
|
|
|
getNextTag(true) + " OK [APPENDUID 1234567 13] (Success)"
|
|
|
|
});
|
|
|
|
|
|
|
|
mFolder.appendMessages(new Message[] {message});
|
|
|
|
mock.close();
|
|
|
|
|
|
|
|
assertEquals("13", message.getUid());
|
|
|
|
assertEquals(7, mFolder.getMessageCount());
|
|
|
|
}
|
2010-05-18 20:41:14 +00:00
|
|
|
|
|
|
|
public void testGetPersonalNamespaces() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
expectLogin(mock, new String[] {"* ID NIL", "OK"});
|
|
|
|
|
|
|
|
mock.expect(getNextTag(false) + " LIST \"\" \"\\*\"",
|
|
|
|
new String[] {
|
|
|
|
"* LIST (\\HasNoChildren) \"/\" \"inbox\"",
|
|
|
|
"* LIST (\\hasnochildren) \"/\" \"Drafts\"",
|
|
|
|
"* LIST (\\noselect) \"/\" \"no select\"",
|
|
|
|
"* LIST (\\HasNoChildren) \"/\" \"&ZeVnLIqe-\"", // Japanese folder name
|
|
|
|
getNextTag(true) + " OK SUCCESS"
|
|
|
|
});
|
|
|
|
Folder[] folders = mStore.getPersonalNamespaces();
|
|
|
|
|
|
|
|
ArrayList<String> list = new ArrayList<String>();
|
|
|
|
for (Folder f : folders) {
|
|
|
|
list.add(f.getName());
|
|
|
|
}
|
|
|
|
MoreAsserts.assertEquals(
|
|
|
|
new String[] {"Drafts", "\u65E5\u672C\u8A9E", "INBOX"},
|
|
|
|
list.toArray(new String[0])
|
|
|
|
);
|
|
|
|
|
|
|
|
// TODO test with path prefix
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testEncodeFolderName() {
|
|
|
|
assertEquals("", ImapStore.encodeFolderName(""));
|
|
|
|
assertEquals("a", ImapStore.encodeFolderName("a"));
|
|
|
|
assertEquals("XYZ", ImapStore.encodeFolderName("XYZ"));
|
|
|
|
assertEquals("&ZeVnLIqe-", ImapStore.encodeFolderName("\u65E5\u672C\u8A9E"));
|
|
|
|
assertEquals("!&ZeVnLIqe-!", ImapStore.encodeFolderName("!\u65E5\u672C\u8A9E!"));
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testDecodeFolderName() {
|
|
|
|
assertEquals("", ImapStore.decodeFolderName(""));
|
|
|
|
assertEquals("a", ImapStore.decodeFolderName("a"));
|
|
|
|
assertEquals("XYZ", ImapStore.decodeFolderName("XYZ"));
|
|
|
|
assertEquals("\u65E5\u672C\u8A9E", ImapStore.decodeFolderName("&ZeVnLIqe-"));
|
|
|
|
assertEquals("!\u65E5\u672C\u8A9E!", ImapStore.decodeFolderName("!&ZeVnLIqe-!"));
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testExists() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
expectLogin(mock, new String[] {"* ID NIL", "OK"});
|
|
|
|
|
|
|
|
// Folder exists
|
|
|
|
Folder folder = mStore.getFolder("\u65E5\u672C\u8A9E");
|
|
|
|
mock.expect(getNextTag(false) + " STATUS \\\"&ZeVnLIqe-\\\" \\(UIDVALIDITY\\)",
|
|
|
|
new String[] {
|
|
|
|
"* STATUS \"&ZeVnLIqe-\" (MESSAGES 10)",
|
|
|
|
getNextTag(true) + " OK SUCCESS"
|
|
|
|
});
|
|
|
|
|
|
|
|
assertTrue(folder.exists());
|
|
|
|
|
|
|
|
// Connection verification
|
|
|
|
mock.expect(getNextTag(false) + " NOOP",
|
|
|
|
new String[] {
|
|
|
|
getNextTag(true) + " OK success"
|
|
|
|
});
|
|
|
|
|
|
|
|
// Doesn't exist
|
|
|
|
folder = mStore.getFolder("no such folder");
|
|
|
|
mock.expect(getNextTag(false) + " STATUS \\\"no such folder\\\" \\(UIDVALIDITY\\)",
|
|
|
|
new String[] {
|
|
|
|
getNextTag(true) + " NO No such folder!"
|
|
|
|
});
|
|
|
|
|
|
|
|
assertFalse(folder.exists());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testCreate() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
expectLogin(mock, new String[] {"* ID NIL", "OK"});
|
|
|
|
|
|
|
|
// Success
|
|
|
|
Folder folder = mStore.getFolder("\u65E5\u672C\u8A9E");
|
|
|
|
|
|
|
|
assertTrue(folder.canCreate(FolderType.HOLDS_MESSAGES));
|
|
|
|
|
|
|
|
mock.expect(getNextTag(false) + " CREATE \\\"&ZeVnLIqe-\\\"",
|
|
|
|
new String[] {
|
|
|
|
getNextTag(true) + " OK Success"
|
|
|
|
});
|
|
|
|
|
|
|
|
assertTrue(folder.create(FolderType.HOLDS_MESSAGES));
|
|
|
|
|
|
|
|
// Connection verification
|
|
|
|
mock.expect(getNextTag(false) + " NOOP",
|
|
|
|
new String[] {
|
|
|
|
getNextTag(true) + " OK success"
|
|
|
|
});
|
|
|
|
|
|
|
|
// Failure
|
|
|
|
mock.expect(getNextTag(false) + " CREATE \\\"&ZeVnLIqe-\\\"",
|
|
|
|
new String[] {
|
|
|
|
getNextTag(true) + " NO Can't create folder"
|
|
|
|
});
|
|
|
|
|
|
|
|
assertFalse(folder.create(FolderType.HOLDS_MESSAGES));
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testCopy() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
|
|
|
|
Folder folderTo = mStore.getFolder("\u65E5\u672C\u8A9E");
|
|
|
|
Message[] messages = new Message[] {
|
|
|
|
mFolder.createMessage("11"),
|
|
|
|
mFolder.createMessage("12"),
|
|
|
|
};
|
|
|
|
|
|
|
|
mock.expect(getNextTag(false) + " UID COPY 11\\,12 \\\"&ZeVnLIqe-\\\"",
|
|
|
|
new String[] {
|
|
|
|
getNextTag(true) + " OK copy completed"
|
|
|
|
});
|
|
|
|
|
|
|
|
mFolder.copyMessages(messages, folderTo, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testGetUnreadMessageCount() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
|
|
|
|
mock.expect(getNextTag(false) + " STATUS \\\"" + FOLDER_ENCODED + "\\\" \\(UNSEEN\\)",
|
|
|
|
new String[] {
|
|
|
|
"* STATUS \"" + FOLDER_ENCODED + "\" (X 1 UNSEEN 123)",
|
|
|
|
getNextTag(true) + " OK copy completed"
|
|
|
|
});
|
|
|
|
|
|
|
|
assertEquals(123, mFolder.getUnreadMessageCount());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testExpunge() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
|
|
|
|
mock.expect(getNextTag(false) + " EXPUNGE",
|
|
|
|
new String[] {
|
|
|
|
getNextTag(true) + " OK success"
|
|
|
|
});
|
|
|
|
|
|
|
|
mFolder.expunge();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testSetFlags() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
|
|
|
|
Message[] messages = new Message[] {
|
|
|
|
mFolder.createMessage("11"),
|
|
|
|
mFolder.createMessage("12"),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Set
|
|
|
|
mock.expect(
|
|
|
|
getNextTag(false) + " UID STORE 11\\,12 \\+FLAGS.SILENT \\(\\\\Flagged \\\\Seen\\)",
|
|
|
|
new String[] {
|
|
|
|
getNextTag(true) + " OK success"
|
|
|
|
});
|
|
|
|
mFolder.setFlags(messages, new Flag[] {Flag.FLAGGED, Flag.SEEN}, true);
|
|
|
|
|
|
|
|
// Clear
|
|
|
|
mock.expect(
|
|
|
|
getNextTag(false) + " UID STORE 11\\,12 \\-FLAGS.SILENT \\(\\\\Deleted\\)",
|
|
|
|
new String[] {
|
|
|
|
getNextTag(true) + " OK success"
|
|
|
|
});
|
|
|
|
mFolder.setFlags(messages, new Flag[] {Flag.DELETED}, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void testGetMessage() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
|
|
|
|
// Found
|
|
|
|
mock.expect(
|
|
|
|
getNextTag(false) + " UID SEARCH UID 123",
|
|
|
|
new String[] {
|
|
|
|
"* SEARCH 123",
|
|
|
|
getNextTag(true) + " OK success"
|
|
|
|
});
|
|
|
|
assertEquals("123", mFolder.getMessage("123").getUid());
|
|
|
|
|
|
|
|
// Not found
|
|
|
|
mock.expect(
|
|
|
|
getNextTag(false) + " UID SEARCH UID 123",
|
|
|
|
new String[] {
|
|
|
|
getNextTag(true) + " NO not found"
|
|
|
|
});
|
|
|
|
assertNull(mFolder.getMessage("123"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Test for getMessages(int, int, MessageRetrievalListener) */
|
|
|
|
public void testGetMessages1() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
|
|
|
|
// Found
|
|
|
|
mock.expect(
|
|
|
|
getNextTag(false) + " UID SEARCH 3:5 NOT DELETED",
|
|
|
|
new String[] {
|
|
|
|
"* SEARCH 3 4",
|
|
|
|
getNextTag(true) + " OK success"
|
|
|
|
});
|
|
|
|
|
|
|
|
checkMessageUids(new String[] {"3", "4"}, mFolder.getMessages(3, 5, null));
|
|
|
|
|
|
|
|
// Not found
|
|
|
|
mock.expect(
|
|
|
|
getNextTag(false) + " UID SEARCH 3:5 NOT DELETED",
|
|
|
|
new String[] {
|
|
|
|
getNextTag(true) + " NO not found"
|
|
|
|
});
|
|
|
|
|
|
|
|
checkMessageUids(new String[] {}, mFolder.getMessages(3, 5, null));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test for getMessages(String[] uids, MessageRetrievalListener) where uids != null.
|
|
|
|
* (testGetMessages3() covers the case where uids == null.)
|
|
|
|
*/
|
|
|
|
public void testGetMessages2() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
|
|
|
|
// No command will be sent
|
|
|
|
checkMessageUids(new String[] {"3", "4", "5"},
|
|
|
|
mFolder.getMessages(new String[] {"3", "4", "5"}, null));
|
|
|
|
|
|
|
|
checkMessageUids(new String[] {},
|
|
|
|
mFolder.getMessages(new String[] {}, null));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test for getMessages(MessageRetrievalListener), which is the same as
|
|
|
|
* getMessages(String[] uids, MessageRetrievalListener) where uids == null.
|
|
|
|
*/
|
|
|
|
public void testGetMessages3() throws Exception {
|
|
|
|
MockTransport mock = openAndInjectMockTransport();
|
|
|
|
setupOpenFolder(mock);
|
|
|
|
mFolder.open(OpenMode.READ_WRITE, null);
|
|
|
|
|
|
|
|
mock.expect(
|
|
|
|
getNextTag(false) + " UID SEARCH 1:\\* NOT DELETED",
|
|
|
|
new String[] {
|
|
|
|
"* SEARCH 3 4 5",
|
|
|
|
getNextTag(true) + " OK success"
|
|
|
|
});
|
|
|
|
checkMessageUids(new String[] {"3", "4", "5"},
|
|
|
|
mFolder.getMessages(null));
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void checkMessageUids(String[] expectedUids, Message[] actualMessages) {
|
|
|
|
ArrayList<String> list = new ArrayList<String>();
|
|
|
|
for (Message m : actualMessages) {
|
|
|
|
list.add(m.getUid());
|
|
|
|
}
|
|
|
|
MoreAsserts.assertEquals(expectedUids, list.toArray(new String[0]) );
|
|
|
|
}
|
2009-03-04 03:32:22 +00:00
|
|
|
}
|