Use Account instead of URI to create transports

There's no need to create a URI just to rip in appart again. Additionally, to
support additional changes (i.e. to use Mailbox instead of Folder in the
MessageController), we need to store the actual Account.

NOTE -- This change only affects IMAP and POP3. SMTP will come in a follow-on CL

Change-Id: I400036a17271c99272fd9c603547dcd713b50b9d
This commit is contained in:
Todd Kennedy 2011-04-19 11:32:06 -07:00
parent ba7652cda0
commit a50fc99b0c
16 changed files with 539 additions and 422 deletions

View File

@ -2788,6 +2788,19 @@ public abstract class EmailContent {
} }
} }
/**
* Returns the login information. [0] is the username and [1] is the password. If
* {@link #FLAG_AUTHENTICATE} is not set, {@code null} is returned.
*/
public String[] getLogin() {
if ((mFlags & FLAG_AUTHENTICATE) != 0) {
String trimUser = (mLogin != null) ? mLogin.trim() : "";
String password = (mPassword != null) ? mPassword : "";
return new String[] { trimUser, password };
}
return null;
}
/** /**
* Sets the connection values of the auth structure per the given scheme, host and port. * Sets the connection values of the auth structure per the given scheme, host and port.
*/ */

View File

@ -13,10 +13,15 @@
public *** newInstance(android.content.Context, java.lang.String); public *** newInstance(android.content.Context, java.lang.String);
} }
# TODO remove after converting Sender#instantiateSender() to use Account instead of URI
-keepclasseswithmembers class * { -keepclasseswithmembers class * {
public *** newInstance(java.lang.String, android.content.Context, com.android.email.mail.Store$PersistentDataCallbacks); public *** newInstance(java.lang.String, android.content.Context, com.android.email.mail.Store$PersistentDataCallbacks);
} }
-keepclasseswithmembers class * {
public *** newInstance(com.android.emailcommon.provider.EmailContent$Account, android.content.Context, com.android.email.mail.Store$PersistentDataCallbacks);
}
-keepclasseswithmembers class android.content.SharedPreferences$Editor { -keepclasseswithmembers class android.content.SharedPreferences$Editor {
*** apply(); *** apply();
} }
@ -144,7 +149,7 @@
*** setTransport(com.android.email.mail.Transport); *** setTransport(com.android.email.mail.Transport);
} }
-keepclasseswithmembers class com.android.email.mail.store.ImapStore$ImapFolder { -keepclasseswithmembers class com.android.email.mail.store.ImapFolder {
*** getMessages(int, int, com.android.emailcommon.mail.Folder$MessageRetrievalListener); *** getMessages(int, int, com.android.emailcommon.mail.Folder$MessageRetrievalListener);
*** getMessages(com.android.emailcommon.mail.Folder$MessageRetrievalListener); *** getMessages(com.android.emailcommon.mail.Folder$MessageRetrievalListener);
*** getMessages(java.lang.String[], com.android.emailcommon.mail.Folder$MessageRetrievalListener); *** getMessages(java.lang.String[], com.android.emailcommon.mail.Folder$MessageRetrievalListener);

View File

@ -1006,7 +1006,7 @@ public class Controller {
} }
/** /**
* Delete an account synchronously. Intended to be used only by unit tests. * Delete an account synchronously.
*/ */
public void deleteAccountSync(long accountId, Context context) { public void deleteAccountSync(long accountId, Context context) {
try { try {
@ -1017,12 +1017,13 @@ public class Controller {
return; // Already deleted? return; // Already deleted?
} }
final String accountUri = account.getStoreUri(context); try {
// Delete Remote store at first. // Delete Remote store at first.
if (!TextUtils.isEmpty(accountUri)) { Store.getInstance(account, context, null).delete();
Store.getInstance(accountUri, context, null).delete();
// Remove the Store instance from cache. // Remove the Store instance from cache.
Store.removeInstance(accountUri); Store.removeInstance(account, context);
} catch (MessagingException e) {
Log.w(Logging.LOG_TAG, "Failed to delete store", e);
} }
Uri uri = ContentUris.withAppendedId( Uri uri = ContentUris.withAppendedId(

View File

@ -253,7 +253,7 @@ public class MessagingController implements Runnable {
// Step 1: Get remote folders, make a list, and add any local folders // Step 1: Get remote folders, make a list, and add any local folders
// that don't already exist. // that don't already exist.
Store store = Store.getInstance(account.getStoreUri(mContext), mContext, null); Store store = Store.getInstance(account, mContext, null);
Folder[] remoteFolders = store.getAllFolders(); Folder[] remoteFolders = store.getAllFolders();
@ -383,7 +383,7 @@ public class MessagingController implements Runnable {
StoreSynchronizer.SyncResults results; StoreSynchronizer.SyncResults results;
// Select generic sync or store-specific sync // Select generic sync or store-specific sync
Store remoteStore = Store.getInstance(account.getStoreUri(mContext), mContext, null); Store remoteStore = Store.getInstance(account, mContext, null);
StoreSynchronizer customSync = remoteStore.getMessageSynchronizer(); StoreSynchronizer customSync = remoteStore.getMessageSynchronizer();
if (customSync == null) { if (customSync == null) {
results = synchronizeMailboxGeneric(account, folder); results = synchronizeMailboxGeneric(account, folder);
@ -512,7 +512,7 @@ public class MessagingController implements Runnable {
// 2. Open the remote folder and create the remote folder if necessary // 2. Open the remote folder and create the remote folder if necessary
Store remoteStore = Store.getInstance(account.getStoreUri(mContext), mContext, null); Store remoteStore = Store.getInstance(account, mContext, null);
Folder remoteFolder = remoteStore.getFolder(folder.mDisplayName); Folder remoteFolder = remoteStore.getFolder(folder.mDisplayName);
/* /*
@ -1130,7 +1130,7 @@ public class MessagingController implements Runnable {
// Load the remote store if it will be needed // Load the remote store if it will be needed
if (remoteStore == null && deleteFromTrash) { if (remoteStore == null && deleteFromTrash) {
remoteStore = Store.getInstance(account.getStoreUri(mContext), mContext, null); remoteStore = Store.getInstance(account, mContext, null);
} }
// Dispatch here for specific change types // Dispatch here for specific change types
@ -1202,7 +1202,7 @@ public class MessagingController implements Runnable {
// Load the remote store if it will be needed // Load the remote store if it will be needed
if (remoteStore == null) { if (remoteStore == null) {
remoteStore = remoteStore =
Store.getInstance(account.getStoreUri(mContext), mContext, null); Store.getInstance(account, mContext, null);
} }
// Load the mailbox if it will be needed // Load the mailbox if it will be needed
if (mailbox == null) { if (mailbox == null) {
@ -1232,7 +1232,7 @@ public class MessagingController implements Runnable {
// Load the remote store if it will be needed // Load the remote store if it will be needed
if (remoteStore == null) { if (remoteStore == null) {
remoteStore = remoteStore =
Store.getInstance(account.getStoreUri(mContext), mContext, null); Store.getInstance(account, mContext, null);
} }
// Load the mailbox if it will be needed // Load the mailbox if it will be needed
if (mailbox == null) { if (mailbox == null) {
@ -1319,7 +1319,7 @@ public class MessagingController implements Runnable {
// Load the remote store if it will be needed // Load the remote store if it will be needed
if (remoteStore == null && if (remoteStore == null &&
(changeMoveToTrash || changeRead || changeFlagged || changeMailbox)) { (changeMoveToTrash || changeRead || changeFlagged || changeMailbox)) {
remoteStore = Store.getInstance(account.getStoreUri(mContext), mContext, null); remoteStore = Store.getInstance(account, mContext, null);
} }
// Dispatch here for specific change types // Dispatch here for specific change types
@ -1829,7 +1829,7 @@ public class MessagingController implements Runnable {
} }
Store remoteStore = Store remoteStore =
Store.getInstance(account.getStoreUri(mContext), mContext, null); Store.getInstance(account, mContext, null);
Folder remoteFolder = remoteStore.getFolder(mailbox.mDisplayName); Folder remoteFolder = remoteStore.getFolder(mailbox.mDisplayName);
remoteFolder.open(OpenMode.READ_WRITE, null); remoteFolder.open(OpenMode.READ_WRITE, null);
@ -1922,7 +1922,7 @@ public class MessagingController implements Runnable {
} }
Store remoteStore = Store remoteStore =
Store.getInstance(account.getStoreUri(mContext), mContext, null); Store.getInstance(account, mContext, null);
Folder remoteFolder = remoteStore.getFolder(mailbox.mDisplayName); Folder remoteFolder = remoteStore.getFolder(mailbox.mDisplayName);
remoteFolder.open(OpenMode.READ_WRITE, null); remoteFolder.open(OpenMode.READ_WRITE, null);
@ -2020,7 +2020,7 @@ public class MessagingController implements Runnable {
mListeners.sendPendingMessagesStarted(account.mId, -1); mListeners.sendPendingMessagesStarted(account.mId, -1);
Sender sender = Sender.getInstance(mContext, account.getSenderUri(mContext)); Sender sender = Sender.getInstance(mContext, account.getSenderUri(mContext));
Store remoteStore = Store.getInstance(account.getStoreUri(mContext), mContext, null); Store remoteStore = Store.getInstance(account, mContext, null);
boolean requireMoveMessageToSentFolder = remoteStore.requireCopyMessageToSentFolder(); boolean requireMoveMessageToSentFolder = remoteStore.requireCopyMessageToSentFolder();
ContentValues moveToSentValues = null; ContentValues moveToSentValues = null;
if (requireMoveMessageToSentFolder) { if (requireMoveMessageToSentFolder) {

View File

@ -390,7 +390,7 @@ public class AccountCheckSettingsFragment extends Fragment {
final Context mContext; final Context mContext;
final int mMode; final int mMode;
final String mStoreUri; final Account mAccount;
final String mStoreHost; final String mStoreHost;
final String mSenderUri; final String mSenderUri;
final String mCheckEmail; final String mCheckEmail;
@ -404,7 +404,7 @@ public class AccountCheckSettingsFragment extends Fragment {
public AccountCheckTask(int mode, Account checkAccount) { public AccountCheckTask(int mode, Account checkAccount) {
mContext = getActivity().getApplicationContext(); mContext = getActivity().getApplicationContext();
mMode = mode; mMode = mode;
mStoreUri = checkAccount.getStoreUri(mContext); mAccount = checkAccount;
mStoreHost = checkAccount.mHostAuthRecv.mAddress; mStoreHost = checkAccount.mHostAuthRecv.mAddress;
mSenderUri = checkAccount.getSenderUri(mContext); mSenderUri = checkAccount.getSenderUri(mContext);
mCheckEmail = checkAccount.mEmailAddress; mCheckEmail = checkAccount.mEmailAddress;
@ -422,7 +422,7 @@ public class AccountCheckSettingsFragment extends Fragment {
if (isCancelled()) return null; if (isCancelled()) return null;
publishProgress(STATE_CHECK_AUTODISCOVER); publishProgress(STATE_CHECK_AUTODISCOVER);
Log.d(Logging.LOG_TAG, "Begin auto-discover for " + mCheckEmail); Log.d(Logging.LOG_TAG, "Begin auto-discover for " + mCheckEmail);
Store store = Store.getInstance(mStoreUri, mContext, null); Store store = Store.getInstance(mAccount, mContext, null);
Bundle result = store.autoDiscover(mContext, mCheckEmail, mCheckPassword); Bundle result = store.autoDiscover(mContext, mCheckEmail, mCheckPassword);
// Result will be one of: // Result will be one of:
// null: remote exception - proceed to manual setup // null: remote exception - proceed to manual setup
@ -450,7 +450,7 @@ public class AccountCheckSettingsFragment extends Fragment {
if (isCancelled()) return null; if (isCancelled()) return null;
Log.d(Logging.LOG_TAG, "Begin check of incoming email settings"); Log.d(Logging.LOG_TAG, "Begin check of incoming email settings");
publishProgress(STATE_CHECK_INCOMING); publishProgress(STATE_CHECK_INCOMING);
Store store = Store.getInstance(mStoreUri, mContext, null); Store store = Store.getInstance(mAccount, mContext, null);
Bundle bundle = store.checkSettings(); Bundle bundle = store.checkSettings();
int resultCode = MessagingException.UNSPECIFIED_EXCEPTION; int resultCode = MessagingException.UNSPECIFIED_EXCEPTION;
if (bundle != null) { if (bundle != null) {

View File

@ -613,7 +613,7 @@ public class AccountSettingsXL extends PreferenceActivity {
*/ */
public void onIncomingSettings(Account account) { public void onIncomingSettings(Account account) {
try { try {
Store store = Store.getInstance(account.getStoreUri(this), getApplication(), null); Store store = Store.getInstance(account, getApplication(), null);
if (store != null) { if (store != null) {
Class<? extends android.app.Activity> setting = store.getSettingActivityClass(); Class<? extends android.app.Activity> setting = store.getSettingActivityClass();
if (setting != null) { if (setting != null) {

View File

@ -21,12 +21,17 @@ import com.android.email.R;
import com.android.emailcommon.Logging; import com.android.emailcommon.Logging;
import com.android.emailcommon.mail.Folder; import com.android.emailcommon.mail.Folder;
import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.HostAuth;
import com.android.emailcommon.utility.Utility;
import com.google.common.annotations.VisibleForTesting;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import android.content.Context; import android.content.Context;
import android.content.res.XmlResourceParser; import android.content.res.XmlResourceParser;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import java.io.IOException; import java.io.IOException;
@ -59,18 +64,20 @@ public abstract class Store {
* should be returned on FetchProfile.Item.BODY_SANE requests. * should be returned on FetchProfile.Item.BODY_SANE requests.
*/ */
public static final int FETCH_BODY_SANE_SUGGESTED_SIZE = (50 * 1024); public static final int FETCH_BODY_SANE_SUGGESTED_SIZE = (50 * 1024);
private static final HashMap<String, Store> sStores = new HashMap<String, Store>(); @VisibleForTesting
static final HashMap<String, Store> sStores = new HashMap<String, Store>();
/** /**
* Static named constructor. It should be overrode by extending class. * Static named constructor. It should be overrode by extending class.
* Because this method will be called through reflection, it can not be protected. * Because this method will be called through reflection, it can not be protected.
*/ */
public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks) public static Store newInstance(Account account, Context context,
throws MessagingException { PersistentDataCallbacks callbacks) throws MessagingException {
throw new MessagingException("Store.newInstance: Unknown scheme in " + uri); throw new MessagingException("Store#newInstance: Unknown scheme in "
+ account.mDisplayName);
} }
private static Store instantiateStore(String className, String uri, Context context, private static Store instantiateStore(String className, Account account, Context context,
PersistentDataCallbacks callbacks) PersistentDataCallbacks callbacks)
throws MessagingException { throws MessagingException {
Object o = null; Object o = null;
@ -78,18 +85,19 @@ public abstract class Store {
Class<?> c = Class.forName(className); Class<?> c = Class.forName(className);
// and invoke "newInstance" class method and instantiate store object. // and invoke "newInstance" class method and instantiate store object.
java.lang.reflect.Method m = java.lang.reflect.Method m =
c.getMethod("newInstance", String.class, Context.class, c.getMethod("newInstance", Account.class, Context.class,
PersistentDataCallbacks.class); PersistentDataCallbacks.class);
o = m.invoke(null, uri, context, callbacks); // TODO Do the stores _really need a context? Is there a way to not pass it along?
o = m.invoke(null, account, context, callbacks);
} catch (Exception e) { } catch (Exception e) {
Log.d(Logging.LOG_TAG, String.format( Log.d(Logging.LOG_TAG, String.format(
"exception %s invoking %s.newInstance.(String, Context) method for %s", "exception %s invoking method %s#newInstance(Account, Context) for %s",
e.toString(), className, uri)); e.toString(), className, account.mDisplayName));
throw new MessagingException("can not instantiate Store object for " + uri); throw new MessagingException("can not instantiate Store for " + account.mDisplayName);
} }
if (!(o instanceof Store)) { if (!(o instanceof Store)) {
throw new MessagingException( throw new MessagingException(
uri + ": " + className + " create incompatible object"); account.mDisplayName + ": " + className + " create incompatible object");
} }
return (Store) o; return (Store) o;
} }
@ -149,38 +157,54 @@ public abstract class Store {
} }
/** /**
* Get an instance of a mail store. The URI is parsed as a standard URI and * Gets a unique key for the given account.
* the scheme is used to determine which protocol will be used. * @throws MessagingException If the account is not setup properly (i.e. there is no address
* * or login)
* Although the URI format is somewhat protocol-specific, we use the following */
* guidelines wherever possible: @VisibleForTesting
* static String getStoreKey(Context context, Account account) throws MessagingException {
* scheme [+ security [+]] :// username : password @ host [ / resource ] final StringBuffer key = new StringBuffer();
* final HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
* Typical schemes include imap, pop3, local, eas. if (recvAuth.mAddress == null) {
* Typical security models include SSL or TLS. throw new MessagingException("Cannot find store for account " + account.mDisplayName);
* A + after the security identifier indicates "required". }
final String address = recvAuth.mAddress.trim();
if (TextUtils.isEmpty(address)) {
throw new MessagingException("Cannot find store for account " + account.mDisplayName);
}
key.append(address);
if (recvAuth.mLogin != null) {
key.append(recvAuth.mLogin.trim());
}
return key.toString();
}
/**
* Get an instance of a mail store for the given account. The account must be valid (i.e. has
* at least an incoming server name).
* *
* Username, password, and host are as expected. * Username, password, and host are as expected.
* Resource is protocol specific. For example, IMAP uses it as the path prefix. EAS uses it * Resource is protocol specific. For example, IMAP uses it as the path prefix. EAS uses it
* as the domain. * as the domain.
* *
* @param uri The URI of the store. * @param account The account of the store.
* @return an initialized store of the appropriate class * @return an initialized store of the appropriate class
* @throws MessagingException * @throws MessagingException If the store cannot be obtained or if the account is invalid.
*/ */
public synchronized static Store getInstance(String uri, Context context, public synchronized static Store getInstance(Account account, Context context,
PersistentDataCallbacks callbacks) throws MessagingException { PersistentDataCallbacks callbacks) throws MessagingException {
Store store = sStores.get(uri); String storeKey = getStoreKey(context, account);
Store store = sStores.get(storeKey);
if (store == null) { if (store == null) {
context = context.getApplicationContext(); Context appContext = context.getApplicationContext();
StoreInfo info = StoreInfo.getStoreInfo(uri, context); HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
StoreInfo info = StoreInfo.getStoreInfo(recvAuth.mProtocol, context);
if (info != null) { if (info != null) {
store = instantiateStore(info.mClassName, uri, context, callbacks); store = instantiateStore(info.mClassName, account, appContext, callbacks);
} }
if (store != null) { if (store != null) {
sStores.put(uri, store); sStores.put(storeKey, store);
} }
} else { } else {
// update the callbacks, which may have been null at creation time. // update the callbacks, which may have been null at creation time.
@ -188,21 +212,25 @@ public abstract class Store {
} }
if (store == null) { if (store == null) {
throw new MessagingException("Unable to locate an applicable Store for " + uri); throw new MessagingException("Cannot find store for account " + account.mDisplayName);
} }
return store; return store;
} }
/** /**
* Delete an instance of a mail store. * Delete the mail store associated with the given account. The account must be valid (i.e. has
* at least an incoming server name).
* *
* The store should have been notified already by calling delete(), and the caller should * The store should have been notified already by calling delete(), and the caller should
* also take responsibility for deleting the matching LocalStore, etc. * also take responsibility for deleting the matching LocalStore, etc.
* @param storeUri the store to be removed *
* @throws MessagingException If the store cannot be removed or if the account is invalid.
*/ */
public synchronized static void removeInstance(String storeUri) { public synchronized static void removeInstance(Account account, Context context)
sStores.remove(storeUri); throws MessagingException {
final String storeKey = getStoreKey(context, account);
sStores.remove(storeKey);
} }
/** /**

View File

@ -60,16 +60,28 @@ public interface Transport {
* *
* @param uri The Uri for the connection * @param uri The Uri for the connection
* @param defaultPort If the Uri does not include an explicit port, this value will be used. * @param defaultPort If the Uri does not include an explicit port, this value will be used.
* @deprecated use the individual methods {@link #setHost(String)} and {@link #setPort(int)}
*/ */
@Deprecated
public void setUri(URI uri, int defaultPort); public void setUri(URI uri, int defaultPort);
/** /**
* @return Returns the host part of the Uri * Sets the host
*/
public void setHost(String host);
/**
* Sets the port
*/
public void setPort(int port);
/**
* Returns the host or {@code null} if none was specified.
*/ */
public String getHost(); public String getHost();
/** /**
* @return Returns the port (either from the Uri or from the default) * Returns the port or {@code 0} if none was specified.
*/ */
public int getPort(); public int getPort();
@ -77,7 +89,9 @@ public interface Transport {
* Returns the user info parts of the Uri, if any were supplied. Typically, [0] is the user * Returns the user info parts of the Uri, if any were supplied. Typically, [0] is the user
* and [1] is the password. * and [1] is the password.
* @return Returns the user info parts of the Uri. Null if none were supplied. * @return Returns the user info parts of the Uri. Null if none were supplied.
* @deprecated do not use this method. user info parts should not be stored in the transport.
*/ */
@Deprecated
public String[] getUserInfoParts(); public String[] getUserInfoParts();
/** /**

View File

@ -37,6 +37,8 @@ import com.android.emailcommon.mail.Flag;
import com.android.emailcommon.mail.Folder; import com.android.emailcommon.mail.Folder;
import com.android.emailcommon.mail.Message; import com.android.emailcommon.mail.Message;
import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.HostAuth;
import com.android.emailcommon.service.EmailServiceProxy; import com.android.emailcommon.service.EmailServiceProxy;
import com.android.emailcommon.utility.Utility; import com.android.emailcommon.utility.Utility;
import com.beetstra.jutf7.CharsetProvider; import com.beetstra.jutf7.CharsetProvider;
@ -51,8 +53,6 @@ import android.util.Log;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.MessageDigest; import java.security.MessageDigest;
@ -130,66 +130,53 @@ public class ImapStore extends Store {
/** /**
* Static named constructor. * Static named constructor.
*/ */
public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks) public static Store newInstance(Account account, Context context,
throws MessagingException { PersistentDataCallbacks callbacks) throws MessagingException {
return new ImapStore(context, uri); return new ImapStore(context, account);
} }
/** /**
* Allowed formats for the Uri: * Creates a new store for the given account.
* imap://user:password@server:port
* imap+tls+://user:password@server:port
* imap+tls+trustallcerts://user:password@server:port
* imap+ssl+://user:password@server:port
* imap+ssl+trustallcerts://user:password@server:port
*
* @param uriString the Uri containing information to configure this store
*/ */
private ImapStore(Context context, String uriString) throws MessagingException { private ImapStore(Context context, Account account) throws MessagingException {
mContext = context; mContext = context;
URI uri;
try {
uri = new URI(uriString);
} catch (URISyntaxException use) {
throw new MessagingException("Invalid ImapStore URI", use);
}
String scheme = uri.getScheme(); HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
if (scheme == null || !scheme.startsWith(STORE_SCHEME_IMAP)) { if (recvAuth == null || !STORE_SCHEME_IMAP.equalsIgnoreCase(recvAuth.mProtocol)) {
throw new MessagingException("Unsupported protocol"); throw new MessagingException("Unsupported protocol");
} }
// defaults, which can be changed by security modifiers // defaults, which can be changed by security modifiers
int connectionSecurity = Transport.CONNECTION_SECURITY_NONE; int connectionSecurity = Transport.CONNECTION_SECURITY_NONE;
int defaultPort = 143; int defaultPort = 143;
// check for security modifiers and apply changes
if (scheme.contains("+ssl")) { // check for security flags and apply changes
if ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) {
connectionSecurity = Transport.CONNECTION_SECURITY_SSL; connectionSecurity = Transport.CONNECTION_SECURITY_SSL;
defaultPort = 993; defaultPort = 993;
} else if (scheme.contains("+tls")) { } else if ((recvAuth.mFlags & HostAuth.FLAG_TLS) != 0) {
connectionSecurity = Transport.CONNECTION_SECURITY_TLS; connectionSecurity = Transport.CONNECTION_SECURITY_TLS;
} }
boolean trustCertificates = scheme.contains(STORE_SECURITY_TRUST_CERTIFICATES); boolean trustCertificates = ((recvAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0);
int port = defaultPort;
if (recvAuth.mPort != HostAuth.PORT_UNKNOWN) {
port = recvAuth.mPort;
}
mRootTransport = new MailTransport("IMAP"); mRootTransport = new MailTransport("IMAP");
mRootTransport.setUri(uri, defaultPort); mRootTransport.setHost(recvAuth.mAddress);
mRootTransport.setPort(port);
mRootTransport.setSecurity(connectionSecurity, trustCertificates); mRootTransport.setSecurity(connectionSecurity, trustCertificates);
String[] userInfoParts = mRootTransport.getUserInfoParts(); String[] userInfoParts = recvAuth.getLogin();
if (userInfoParts != null) { if (userInfoParts != null) {
mUsername = userInfoParts[0]; mUsername = userInfoParts[0];
if (userInfoParts.length > 1) { mPassword = userInfoParts[1];
mPassword = userInfoParts[1];
// build the LOGIN string once (instead of over-and-over again.) // build the LOGIN string once (instead of over-and-over again.)
// apply the quoting here around the built-up password // apply the quoting here around the built-up password
mLoginPhrase = ImapConstants.LOGIN + " " + mUsername + " " mLoginPhrase = ImapConstants.LOGIN + " " + mUsername + " "
+ ImapUtility.imapQuoted(mPassword); + ImapUtility.imapQuoted(mPassword);
}
}
if ((uri.getPath() != null) && (uri.getPath().length() > 0)) {
mPathPrefix = uri.getPath().substring(1);
} }
mPathPrefix = recvAuth.mDomain;
} }
/* package */ Collection<ImapConnection> getConnectionPoolForTest() { /* package */ Collection<ImapConnection> getConnectionPoolForTest() {

View File

@ -29,6 +29,8 @@ import com.android.emailcommon.mail.Folder;
import com.android.emailcommon.mail.Message; import com.android.emailcommon.mail.Message;
import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.mail.Folder.OpenMode; import com.android.emailcommon.mail.Folder.OpenMode;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.HostAuth;
import com.android.emailcommon.service.EmailServiceProxy; import com.android.emailcommon.service.EmailServiceProxy;
import com.android.emailcommon.utility.LoggingInputStream; import com.android.emailcommon.utility.LoggingInputStream;
import com.android.emailcommon.utility.Utility; import com.android.emailcommon.utility.Utility;
@ -39,8 +41,6 @@ import android.util.Log;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -86,54 +86,45 @@ public class Pop3Store extends Store {
/** /**
* Static named constructor. * Static named constructor.
*/ */
public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks) public static Store newInstance(Account account, Context context,
throws MessagingException { PersistentDataCallbacks callbacks) throws MessagingException {
return new Pop3Store(uri); return new Pop3Store(context, account);
} }
/** /**
* pop3://user:password@server:port * Creates a new store for the given account.
* pop3+tls+://user:password@server:port
* pop3+tls+trustallcerts://user:password@server:port
* pop3+ssl+://user:password@server:port
* pop3+ssl+trustallcerts://user:password@server:port
*
* @param _uri
*/ */
private Pop3Store(String _uri) throws MessagingException { private Pop3Store(Context context, Account account) throws MessagingException {
URI uri; HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
try { if (recvAuth == null || !STORE_SCHEME_POP3.equalsIgnoreCase(recvAuth.mProtocol)) {
uri = new URI(_uri);
} catch (URISyntaxException use) {
throw new MessagingException("Invalid Pop3Store URI", use);
}
String scheme = uri.getScheme();
if (scheme == null || !scheme.startsWith(STORE_SCHEME_POP3)) {
throw new MessagingException("Unsupported protocol"); throw new MessagingException("Unsupported protocol");
} }
// defaults, which can be changed by security modifiers // defaults, which can be changed by security modifiers
int connectionSecurity = Transport.CONNECTION_SECURITY_NONE; int connectionSecurity = Transport.CONNECTION_SECURITY_NONE;
int defaultPort = 110; int defaultPort = 110;
// check for security modifiers and apply changes
if (scheme.contains("+ssl")) { // check for security flags and apply changes
if ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) {
connectionSecurity = Transport.CONNECTION_SECURITY_SSL; connectionSecurity = Transport.CONNECTION_SECURITY_SSL;
defaultPort = 995; defaultPort = 995;
} else if (scheme.contains("+tls")) { } else if ((recvAuth.mFlags & HostAuth.FLAG_TLS) != 0) {
connectionSecurity = Transport.CONNECTION_SECURITY_TLS; connectionSecurity = Transport.CONNECTION_SECURITY_TLS;
} }
boolean trustCertificates = scheme.contains(STORE_SECURITY_TRUST_CERTIFICATES); boolean trustCertificates = ((recvAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0);
int port = defaultPort;
if (recvAuth.mPort != HostAuth.PORT_UNKNOWN) {
port = recvAuth.mPort;
}
mTransport = new MailTransport("POP3"); mTransport = new MailTransport("POP3");
mTransport.setUri(uri, defaultPort); mTransport.setHost(recvAuth.mAddress);
mTransport.setPort(port);
mTransport.setSecurity(connectionSecurity, trustCertificates); mTransport.setSecurity(connectionSecurity, trustCertificates);
String[] userInfoParts = mTransport.getUserInfoParts(); String[] userInfoParts = recvAuth.getLogin();
if (userInfoParts != null) { if (userInfoParts != null) {
mUsername = userInfoParts[0]; mUsername = userInfoParts[0];
if (userInfoParts.length > 1) { mPassword = userInfoParts[1];
mPassword = userInfoParts[1];
}
} }
} }
@ -147,7 +138,7 @@ public class Pop3Store extends Store {
} }
@Override @Override
public Folder getFolder(String name) throws MessagingException { public Folder getFolder(String name) {
Folder folder = mFolders.get(name); Folder folder = mFolders.get(name);
if (folder == null) { if (folder == null) {
folder = new Pop3Folder(name); folder = new Pop3Folder(name);
@ -157,7 +148,7 @@ public class Pop3Store extends Store {
} }
@Override @Override
public Folder[] getAllFolders() throws MessagingException { public Folder[] getAllFolders() {
return new Folder[] { return new Folder[] {
getFolder("INBOX"), getFolder("INBOX"),
}; };
@ -314,7 +305,7 @@ public class Pop3Store extends Store {
} }
@Override @Override
public OpenMode getMode() throws MessagingException { public OpenMode getMode() {
return OpenMode.READ_WRITE; return OpenMode.READ_WRITE;
} }
@ -348,12 +339,12 @@ public class Pop3Store extends Store {
} }
@Override @Override
public boolean create(FolderType type) throws MessagingException { public boolean create(FolderType type) {
return false; return false;
} }
@Override @Override
public boolean exists() throws MessagingException { public boolean exists() {
return mName.equalsIgnoreCase("INBOX"); return mName.equalsIgnoreCase("INBOX");
} }
@ -363,7 +354,7 @@ public class Pop3Store extends Store {
} }
@Override @Override
public int getUnreadMessageCount() throws MessagingException { public int getUnreadMessageCount() {
return -1; return -1;
} }
@ -608,14 +599,15 @@ public class Pop3Store extends Store {
} }
@Override @Override
public Message[] getMessages(MessageRetrievalListener listener) throws MessagingException { public Message[] getMessages(MessageRetrievalListener listener) {
throw new UnsupportedOperationException("Pop3Folder.getMessage(MessageRetrievalListener)"); throw new UnsupportedOperationException(
"Pop3Folder.getMessage(MessageRetrievalListener)");
} }
@Override @Override
public Message[] getMessages(String[] uids, MessageRetrievalListener listener) public Message[] getMessages(String[] uids, MessageRetrievalListener listener) {
throws MessagingException { throw new UnsupportedOperationException(
throw new UnsupportedOperationException("Pop3Folder.getMessage(MessageRetrievalListener)"); "Pop3Folder.getMessage(MessageRetrievalListener)");
} }
/** /**
@ -805,20 +797,20 @@ public class Pop3Store extends Store {
} }
@Override @Override
public Flag[] getPermanentFlags() throws MessagingException { public Flag[] getPermanentFlags() {
return PERMANENT_FLAGS; return PERMANENT_FLAGS;
} }
@Override @Override
public void appendMessages(Message[] messages) throws MessagingException { public void appendMessages(Message[] messages) {
} }
@Override @Override
public void delete(boolean recurse) throws MessagingException { public void delete(boolean recurse) {
} }
@Override @Override
public Message[] expunge() throws MessagingException { public Message[] expunge() {
return null; return null;
} }
@ -847,8 +839,7 @@ public class Pop3Store extends Store {
} }
@Override @Override
public void copyMessages(Message[] msgs, Folder folder, MessageUpdateCallbacks callbacks) public void copyMessages(Message[] msgs, Folder folder, MessageUpdateCallbacks callbacks) {
throws MessagingException {
throw new UnsupportedOperationException("copyMessages is not supported in POP3"); throw new UnsupportedOperationException("copyMessages is not supported in POP3");
} }
@ -859,7 +850,7 @@ public class Pop3Store extends Store {
// (mMessageCount * 58) / (mThroughputKbS * 1024 / 8) * 1000; // (mMessageCount * 58) / (mThroughputKbS * 1024 / 8) * 1000;
// } // }
private Pop3Capabilities getCapabilities() throws IOException, MessagingException { private Pop3Capabilities getCapabilities() throws IOException {
Pop3Capabilities capabilities = new Pop3Capabilities(); Pop3Capabilities capabilities = new Pop3Capabilities();
try { try {
String response = executeSimpleCommand("CAPA"); String response = executeSimpleCommand("CAPA");
@ -945,13 +936,13 @@ public class Pop3Store extends Store {
} }
@Override @Override
public Message createMessage(String uid) throws MessagingException { public Message createMessage(String uid) {
return new Pop3Message(uid, this); return new Pop3Message(uid, this);
} }
} }
public static class Pop3Message extends MimeMessage { public static class Pop3Message extends MimeMessage {
public Pop3Message(String uid, Pop3Folder folder) throws MessagingException { public Pop3Message(String uid, Pop3Folder folder) {
mUid = uid; mUid = uid;
mFolder = folder; mFolder = folder;
mSize = -1; mSize = -1;

View File

@ -98,6 +98,8 @@ public class MailTransport implements Transport {
return newObject; return newObject;
} }
@Override
@Deprecated
public void setUri(URI uri, int defaultPort) { public void setUri(URI uri, int defaultPort) {
mHost = uri.getHost(); mHost = uri.getHost();
@ -109,13 +111,24 @@ public class MailTransport implements Transport {
if (uri.getUserInfo() != null) { if (uri.getUserInfo() != null) {
mUserInfoParts = uri.getUserInfo().split(":", 2); mUserInfoParts = uri.getUserInfo().split(":", 2);
} }
} }
@Override
@Deprecated
public String[] getUserInfoParts() { public String[] getUserInfoParts() {
return mUserInfoParts; return mUserInfoParts;
} }
@Override
public void setHost(String host) {
mHost = host;
}
@Override
public void setPort(int port) {
mPort = port;
}
public String getHost() { public String getHost() {
return mHost; return mHost;
} }

View File

@ -40,6 +40,7 @@ import javax.net.ssl.SSLException;
/** /**
* This class handles all of the protocol-level aspects of sending messages via SMTP. * This class handles all of the protocol-level aspects of sending messages via SMTP.
* TODO Remove dependence upon URI; there's no reason why we need it here
*/ */
public class SmtpSender extends Sender { public class SmtpSender extends Sender {
@ -65,6 +66,7 @@ public class SmtpSender extends Sender {
* *
* @param uriString the Uri containing information to configure this sender * @param uriString the Uri containing information to configure this sender
*/ */
@SuppressWarnings("deprecation")
private SmtpSender(Context context, String uriString) throws MessagingException { private SmtpSender(Context context, String uriString) throws MessagingException {
mContext = context; mContext = context;
URI uri; URI uri;

View File

@ -17,7 +17,10 @@
package com.android.email.mail; package com.android.email.mail;
import com.android.email.Email; import com.android.email.Email;
import com.android.email.mail.Store.StoreInfo;
import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.HostAuth;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.MediumTest;
@ -27,83 +30,100 @@ import android.test.suitebuilder.annotation.MediumTest;
*/ */
@MediumTest @MediumTest
public class StoreTests extends AndroidTestCase { public class StoreTests extends AndroidTestCase {
public void testGetStoreKey() throws MessagingException {
HostAuth testAuth = new HostAuth();
Account testAccount = new Account();
String testKey;
/** // Make sure to set the host auth; otherwise we create entries in the hostauth db
* Test StoreInfo & Store lookup for POP accounts testAccount.mHostAuthRecv = testAuth;
*/
public void testStoreLookupPOP() throws MessagingException {
final String storeUri = "pop3://user:password@server.com";
Store.StoreInfo info = Store.StoreInfo.getStoreInfo(storeUri, getContext());
assertNotNull("storeInfo null", info);
assertNotNull("scheme null", info.mScheme);
assertNotNull("classname null", info.mClassName);
assertFalse(info.mPushSupported);
assertEquals(Email.VISIBLE_LIMIT_DEFAULT, info.mVisibleLimitDefault);
assertEquals(Email.VISIBLE_LIMIT_INCREMENT, info.mVisibleLimitIncrement);
// This will throw MessagingException if the result would have been null
Store store = Store.getInstance(storeUri, getContext(), null);
}
/**
* Test StoreInfo & Store lookup for IMAP accounts
*/
public void testStoreLookupIMAP() throws MessagingException {
final String storeUri = "imap://user:password@server.com";
Store.StoreInfo info = Store.StoreInfo.getStoreInfo(storeUri, getContext());
assertNotNull("storeInfo null", info);
assertNotNull("scheme null", info.mScheme);
assertNotNull("classname null", info.mClassName);
assertFalse(info.mPushSupported);
assertEquals(Email.VISIBLE_LIMIT_DEFAULT, info.mVisibleLimitDefault);
assertEquals(Email.VISIBLE_LIMIT_INCREMENT, info.mVisibleLimitIncrement);
// This will throw MessagingException if the result would have been null
Store store = Store.getInstance(storeUri, getContext(), null);
}
/**
* Test StoreInfo & Store lookup for EAS accounts
* TODO: EAS store will probably require implementation of Store.PersistentDataCallbacks
*/
public void testStoreLookupEAS() throws MessagingException {
final String storeUri = "eas://user:password@server.com";
Store.StoreInfo info = Store.StoreInfo.getStoreInfo(storeUri, getContext());
if (info != null) {
assertNotNull("scheme null", info.mScheme);
assertNotNull("classname null", info.mClassName);
assertTrue(info.mPushSupported);
assertEquals(-1, info.mVisibleLimitDefault);
assertEquals(-1, info.mVisibleLimitIncrement);
// This will throw MessagingException if the result would have been null
Store store = Store.getInstance(storeUri, getContext(), null);
} else {
try {
Store store = Store.getInstance(storeUri, getContext(), null);
fail("MessagingException expected when EAS not supported");
} catch (MessagingException me) {
// expected - fall through
}
}
}
/**
* Test StoreInfo & Store lookup for unknown accounts
*/
public void testStoreLookupUnknown() {
final String storeUri = "bogus-scheme://user:password@server.com";
Store.StoreInfo info = Store.StoreInfo.getStoreInfo(storeUri, getContext());
assertNull(info);
// No address defined; throws an exception
try { try {
Store store = Store.getInstance(storeUri, getContext(), null); testKey = Store.getStoreKey(mContext, testAccount);
fail("MessagingException expected from bogus URI scheme"); fail("MesasginException not thrown for missing address");
} catch (MessagingException me) { } catch (MessagingException expected) {
// expected - fall through
} }
// Empty address defined; throws an exception
testAuth.mAddress = " \t ";
try {
testKey = Store.getStoreKey(mContext, testAccount);
fail("MesasginException not thrown for empty address");
} catch (MessagingException expected) {
}
// Address defined, no login
testAuth.mAddress = "a.valid.address.com";
testKey = Store.getStoreKey(mContext, testAccount);
assertEquals("a.valid.address.com", testKey);
// Address & login defined
testAuth.mAddress = "address.org";
testAuth.mLogin = "auser";
testKey = Store.getStoreKey(mContext, testAccount);
assertEquals("address.orgauser", testKey);
}
public void testGetStoreInfo() {
StoreInfo testInfo;
// POP3
testInfo = Store.StoreInfo.getStoreInfo("pop3", mContext);
assertNotNull(testInfo);
assertNotNull(testInfo.mScheme);
assertNotNull(testInfo.mClassName);
assertFalse(testInfo.mPushSupported);
assertEquals(Email.VISIBLE_LIMIT_DEFAULT, testInfo.mVisibleLimitDefault);
assertEquals(Email.VISIBLE_LIMIT_INCREMENT, testInfo.mVisibleLimitIncrement);
// IMAP
testInfo = Store.StoreInfo.getStoreInfo("imap", mContext);
assertNotNull(testInfo);
assertNotNull(testInfo.mScheme);
assertNotNull(testInfo.mClassName);
assertFalse(testInfo.mPushSupported);
assertEquals(Email.VISIBLE_LIMIT_DEFAULT, testInfo.mVisibleLimitDefault);
assertEquals(Email.VISIBLE_LIMIT_INCREMENT, testInfo.mVisibleLimitIncrement);
// Unknown
testInfo = Store.StoreInfo.getStoreInfo("unknownscheme", mContext);
assertNull(testInfo);
}
public void testGetInstance() throws MessagingException {
HostAuth testAuth = new HostAuth();
Account testAccount = new Account();
Store testStore;
// Make sure to set the host auth; otherwise we create entries in the hostauth db
testAccount.mHostAuthRecv = testAuth;
// POP3
testAuth.mAddress = "pop3.google.com";
testAuth.mProtocol = "pop3";
testStore = Store.getInstance(testAccount, getContext(), null);
assertEquals(1, Store.sStores.size());
assertSame(testStore, Store.sStores.get("pop3.google.com"));
Store.sStores.clear();
// IMAP
testAuth.mAddress = "imap.google.com";
testAuth.mProtocol = "imap";
testStore = Store.getInstance(testAccount, getContext(), null);
assertEquals(1, Store.sStores.size());
assertSame(testStore, Store.sStores.get("imap.google.com"));
Store.sStores.clear();
// Unknown
testAuth.mAddress = "unknown.google.com";
testAuth.mProtocol = "unknown";
try {
testStore = Store.getInstance(testAccount, getContext(), null);
fail("Store#getInstance() should have thrown an exception");
} catch (MessagingException expected) {
}
assertEquals(0, Store.sStores.size());
} }
} }

View File

@ -41,12 +41,15 @@ import com.android.emailcommon.mail.Message;
import com.android.emailcommon.mail.Message.RecipientType; import com.android.emailcommon.mail.Message.RecipientType;
import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.mail.Part; import com.android.emailcommon.mail.Part;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.HostAuth;
import com.android.emailcommon.utility.Utility; import com.android.emailcommon.utility.Utility;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.test.AndroidTestCase; import android.test.InstrumentationTestCase;
import android.test.MoreAsserts; import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.SmallTest;
@ -66,7 +69,7 @@ import java.util.regex.Pattern;
* TODO test for BYE response in various places? * TODO test for BYE response in various places?
*/ */
@SmallTest @SmallTest
public class ImapStoreUnitTests extends AndroidTestCase { public class ImapStoreUnitTests extends InstrumentationTestCase {
private final static String[] NO_REPLY = new String[0]; private final static String[] NO_REPLY = new String[0];
/** /**
@ -85,6 +88,7 @@ public class ImapStoreUnitTests extends AndroidTestCase {
/* These values are provided by setUp() */ /* These values are provided by setUp() */
private ImapStore mStore = null; private ImapStore mStore = null;
private ImapFolder mFolder = null; private ImapFolder mFolder = null;
private Context mTestContext;
private int mNextTag; private int mNextTag;
// Fields specific to the CopyMessages tests // Fields specific to the CopyMessages tests
@ -98,11 +102,18 @@ public class ImapStoreUnitTests extends AndroidTestCase {
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
TempDirectory.setTempDirectory(getContext()); mTestContext = getInstrumentation().getContext();
// Use the target's (i.e. the Email application) context
TempDirectory.setTempDirectory(getInstrumentation().getTargetContext());
// These are needed so we can get at the inner classes // These are needed so we can get at the inner classes
mStore = (ImapStore) ImapStore.newInstance("imap://user:password@server:999", HostAuth testAuth = new HostAuth();
getContext(), null); Account testAccount = new Account();
testAuth.setLogin("user", "password");
testAuth.setConnection("imap", "server", 999);
testAccount.mHostAuthRecv = testAuth;
mStore = (ImapStore) ImapStore.newInstance(testAccount, mTestContext, null);
mFolder = (ImapFolder) mStore.getFolder(FOLDER_NAME); mFolder = (ImapFolder) mStore.getFolder(FOLDER_NAME);
mNextTag = 1; mNextTag = 1;
} }
@ -194,10 +205,10 @@ public class ImapStoreUnitTests extends AndroidTestCase {
// x-android-device-model Model (Optional, so not tested here) // x-android-device-model Model (Optional, so not tested here)
// x-android-net-operator Carrier (Unreliable, so not tested here) // x-android-net-operator Carrier (Unreliable, so not tested here)
// AGUID A device+account UID // AGUID A device+account UID
String id = ImapStore.getImapId(getContext(), "user-name", "host-name", String id = ImapStore.getImapId(mTestContext, "user-name", "host-name",
CAPABILITY_RESPONSE.flatten()); CAPABILITY_RESPONSE.flatten());
HashMap<String, String> map = tokenizeImapId(id); HashMap<String, String> map = tokenizeImapId(id);
assertEquals(getContext().getPackageName(), map.get("name")); assertEquals(mTestContext.getPackageName(), map.get("name"));
assertEquals("android", map.get("os")); assertEquals("android", map.get("os"));
assertNotNull(map.get("os-version")); assertNotNull(map.get("os-version"));
assertNotNull(map.get("vendor")); assertNotNull(map.get("vendor"));
@ -237,7 +248,7 @@ public class ImapStoreUnitTests extends AndroidTestCase {
*/ */
public void testImapIdWithVendorPolicy() { public void testImapIdWithVendorPolicy() {
try { try {
MockVendorPolicy.inject(getContext()); MockVendorPolicy.inject(mTestContext);
// Prepare mock result // Prepare mock result
Bundle result = new Bundle(); Bundle result = new Bundle();
@ -245,7 +256,7 @@ public class ImapStoreUnitTests extends AndroidTestCase {
MockVendorPolicy.mockResult = result; MockVendorPolicy.mockResult = result;
// Invoke // Invoke
String id = ImapStore.getImapId(getContext(), "user-name", "host-name", String id = ImapStore.getImapId(mTestContext, "user-name", "host-name",
ImapTestUtils.parseResponse("* CAPABILITY IMAP4rev1 XXX YYY Z").flatten()); ImapTestUtils.parseResponse("* CAPABILITY IMAP4rev1 XXX YYY Z").flatten());
// Check the result // Check the result
@ -289,17 +300,37 @@ public class ImapStoreUnitTests extends AndroidTestCase {
* Test that IMAP ID uid's are per-username * Test that IMAP ID uid's are per-username
*/ */
public void testImapIdDeviceId() throws MessagingException { public void testImapIdDeviceId() throws MessagingException {
ImapStore store1a = (ImapStore) ImapStore.newInstance("imap://user1:password@server:999", HostAuth testAuth;
getContext(), null); Account testAccount;
ImapStore store1b = (ImapStore) ImapStore.newInstance("imap://user1:password@server:999",
getContext(), null); // store 1a
ImapStore store2 = (ImapStore) ImapStore.newInstance("imap://user2:password@server:999", testAuth = new HostAuth();
getContext(), null); testAuth.setLogin("user1", "password");
testAuth.setConnection("imap", "server", 999);
testAccount = new Account();
testAccount.mHostAuthRecv = testAuth;
ImapStore testStore1A = (ImapStore) ImapStore.newInstance(testAccount, mTestContext, null);
// store 1b
testAuth = new HostAuth();
testAuth.setLogin("user1", "password");
testAuth.setConnection("imap", "server", 999);
testAccount = new Account();
testAccount.mHostAuthRecv = testAuth;
ImapStore testStore1B = (ImapStore) ImapStore.newInstance(testAccount, mTestContext, null);
// store 2
testAuth = new HostAuth();
testAuth.setLogin("user2", "password");
testAuth.setConnection("imap", "server", 999);
testAccount = new Account();
testAccount.mHostAuthRecv = testAuth;
ImapStore testStore2 = (ImapStore) ImapStore.newInstance(testAccount, mTestContext, null);
String capabilities = CAPABILITY_RESPONSE.flatten(); String capabilities = CAPABILITY_RESPONSE.flatten();
String id1a = ImapStore.getImapId(getContext(), "user1", "host-name", capabilities); String id1a = ImapStore.getImapId(mTestContext, "user1", "host-name", capabilities);
String id1b = ImapStore.getImapId(getContext(), "user1", "host-name", capabilities); String id1b = ImapStore.getImapId(mTestContext, "user1", "host-name", capabilities);
String id2 = ImapStore.getImapId(getContext(), "user2", "host-name", capabilities); String id2 = ImapStore.getImapId(mTestContext, "user2", "host-name", capabilities);
String uid1a = tokenizeImapId(id1a).get("AGUID"); String uid1a = tokenizeImapId(id1a).get("AGUID");
String uid1b = tokenizeImapId(id1b).get("AGUID"); String uid1b = tokenizeImapId(id1b).get("AGUID");
@ -412,7 +443,7 @@ public class ImapStoreUnitTests extends AndroidTestCase {
public void testImapIdSecureServerNotSent() throws MessagingException { public void testImapIdSecureServerNotSent() throws MessagingException {
// Note, this is injected into mStore (which we don't use for this test) // Note, this is injected into mStore (which we don't use for this test)
MockTransport mockTransport = openAndInjectMockTransport(); MockTransport mockTransport = openAndInjectMockTransport();
mockTransport.setMockHost("eMail.sEcurEserVer.nEt"); mockTransport.setHost("eMail.sEcurEserVer.nEt");
// Prime the expects pump as if the server wants IMAP ID, but we should not actually expect // Prime the expects pump as if the server wants IMAP ID, but we should not actually expect
// to send it, because the login code in the store should never actually send it (to this // to send it, because the login code in the store should never actually send it (to this
@ -498,7 +529,7 @@ public class ImapStoreUnitTests extends AndroidTestCase {
// Create mock transport and inject it into the ImapStore that's already set up // Create mock transport and inject it into the ImapStore that's already set up
MockTransport mockTransport = new MockTransport(); MockTransport mockTransport = new MockTransport();
mockTransport.setSecurity(connectionSecurity, trustAllCertificates); mockTransport.setSecurity(connectionSecurity, trustAllCertificates);
mockTransport.setMockHost("mock.server.com"); mockTransport.setHost("mock.server.com");
mStore.setTransport(mockTransport); mStore.setTransport(mockTransport);
return mockTransport; return mockTransport;
} }

View File

@ -29,8 +29,10 @@ import com.android.emailcommon.mail.Folder.OpenMode;
import com.android.emailcommon.mail.Message; import com.android.emailcommon.mail.Message;
import com.android.emailcommon.mail.Message.RecipientType; import com.android.emailcommon.mail.Message.RecipientType;
import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.HostAuth;
import android.test.AndroidTestCase; import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.SmallTest;
/** /**
@ -38,8 +40,7 @@ import android.test.suitebuilder.annotation.SmallTest;
* complete - no server(s) required. * complete - no server(s) required.
*/ */
@SmallTest @SmallTest
public class Pop3StoreUnitTests extends AndroidTestCase { public class Pop3StoreUnitTests extends InstrumentationTestCase {
final String UNIQUE_ID_1 = "20080909002219r1800rrjo9e00"; final String UNIQUE_ID_1 = "20080909002219r1800rrjo9e00";
final static int PER_MESSAGE_SIZE = 100; final static int PER_MESSAGE_SIZE = 100;
@ -54,11 +55,18 @@ public class Pop3StoreUnitTests extends AndroidTestCase {
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
TempDirectory.setTempDirectory(getContext()); // Use the target's (i.e. the Email application) context
TempDirectory.setTempDirectory(getInstrumentation().getTargetContext());
// These are needed so we can get at the inner classes // These are needed so we can get at the inner classes
mStore = (Pop3Store) Pop3Store.newInstance("pop3://user:password@server:999", HostAuth testAuth = new HostAuth();
getContext(), null); Account testAccount = new Account();
testAuth.setLogin("user", "password");
testAuth.setConnection("pop3", "server", 999);
testAccount.mHostAuthRecv = testAuth;
mStore = (Pop3Store) Pop3Store.newInstance(
testAccount, getInstrumentation().getContext(), null);
mFolder = (Pop3Store.Pop3Folder) mStore.getFolder("INBOX"); mFolder = (Pop3Store.Pop3Folder) mStore.getFolder("INBOX");
} }
@ -238,7 +246,7 @@ public class Pop3StoreUnitTests extends AndroidTestCase {
/** /**
* Test small Store & Folder functions that manage folders & namespace * Test small Store & Folder functions that manage folders & namespace
*/ */
public void testStoreFoldersFunctions() throws MessagingException { public void testStoreFoldersFunctions() {
// getPersonalNamespaces() always returns INBOX folder // getPersonalNamespaces() always returns INBOX folder
Folder[] folders = mStore.getAllFolders(); Folder[] folders = mStore.getAllFolders();
@ -261,7 +269,7 @@ public class Pop3StoreUnitTests extends AndroidTestCase {
/** /**
* Test small Folder functions that don't really do anything in Pop3 * Test small Folder functions that don't really do anything in Pop3
*/ */
public void testSmallFolderFunctions() throws MessagingException { public void testSmallFolderFunctions() {
// getMode() returns OpenMode.READ_WRITE // getMode() returns OpenMode.READ_WRITE
assertEquals(OpenMode.READ_WRITE, mFolder.getMode()); assertEquals(OpenMode.READ_WRITE, mFolder.getMode());
@ -318,7 +326,7 @@ public class Pop3StoreUnitTests extends AndroidTestCase {
/** /**
* Lightweight test to confirm that POP3 hasn't implemented any folder roles yet. * Lightweight test to confirm that POP3 hasn't implemented any folder roles yet.
*/ */
public void testNoFolderRolesYet() throws MessagingException { public void testNoFolderRolesYet() {
Folder[] remoteFolders = mStore.getAllFolders(); Folder[] remoteFolders = mStore.getAllFolders();
for (Folder folder : remoteFolders) { for (Folder folder : remoteFolders) {
assertEquals(Folder.FolderRole.UNKNOWN, folder.getRole()); assertEquals(Folder.FolderRole.UNKNOWN, folder.getRole());
@ -774,7 +782,7 @@ public class Pop3StoreUnitTests extends AndroidTestCase {
* This test confirms that Pop3Store needs to call close() in the second IOExceptionHandler in * This test confirms that Pop3Store needs to call close() in the second IOExceptionHandler in
* Pop3Folder.open() (when it calls STAT, and there is no response at all). * Pop3Folder.open() (when it calls STAT, and there is no response at all).
*/ */
public void testCatchClosed6b() throws MessagingException { public void testCatchClosed6b() {
// TODO cannot write this test until we can inject stream closures mid-sequence // TODO cannot write this test until we can inject stream closures mid-sequence
} }

View File

@ -204,11 +204,8 @@ public class MockTransport implements Transport {
mPairs.clear(); mPairs.clear();
} }
/** @Override
* This is a test function (not part of the interface) and is used to set up a result public void setHost(String host) {
* value for getHost(), if needed for the test.
*/
public void setMockHost(String host) {
mHost = host; mHost = host;
} }
@ -239,6 +236,11 @@ public class MockTransport implements Transport {
return new MockOutputStream(); return new MockOutputStream();
} }
@Override
public void setPort(int port) {
SmtpSenderUnitTests.fail("setPort() not implemented");
}
public int getPort() { public int getPort() {
SmtpSenderUnitTests.fail("getPort() not implemented"); SmtpSenderUnitTests.fail("getPort() not implemented");
return 0; return 0;
@ -248,6 +250,7 @@ public class MockTransport implements Transport {
return mConnectionSecurity; return mConnectionSecurity;
} }
@Deprecated
public String[] getUserInfoParts() { public String[] getUserInfoParts() {
SmtpSenderUnitTests.fail("getUserInfoParts() not implemented"); SmtpSenderUnitTests.fail("getUserInfoParts() not implemented");
return null; return null;
@ -315,6 +318,7 @@ public class MockTransport implements Transport {
public void setSoTimeout(int timeoutMilliseconds) /* throws SocketException */ { public void setSoTimeout(int timeoutMilliseconds) /* throws SocketException */ {
} }
@Deprecated
public void setUri(URI uri, int defaultPort) { public void setUri(URI uri, int defaultPort) {
SmtpSenderUnitTests.assertTrue("Don't call setUri on a mock transport", false); SmtpSenderUnitTests.assertTrue("Don't call setUri on a mock transport", false);
} }