From 9fd722d7cd98b0af2fe5898f09080b317c26fd8e Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Fri, 12 Dec 2014 03:23:32 +0000 Subject: [PATCH 01/29] Split message code into Local/Remote The remote network code does not need to be aware of concepts like Accounts etc. --- src/com/fsck/k9/Account.java | 8 +- src/com/fsck/k9/Preferences.java | 3 +- src/com/fsck/k9/activity/Accounts.java | 10 +- src/com/fsck/k9/activity/MessageCompose.java | 30 ++- .../fsck/k9/activity/MessageInfoHolder.java | 5 +- src/com/fsck/k9/activity/MessageList.java | 12 +- .../fsck/k9/activity/MessageReference.java | 8 +- .../k9/activity/setup/AccountSetupBasics.java | 4 +- .../activity/setup/AccountSetupIncoming.java | 5 +- src/com/fsck/k9/cache/EmailProviderCache.java | 10 +- .../controller/MessageRetrievalListener.java | 4 +- .../k9/controller/MessagingController.java | 86 ++++--- .../fsck/k9/controller/MessagingListener.java | 25 +-- .../fsck/k9/fragment/MessageListFragment.java | 163 +++++++------- .../fsck/k9/fragment/MessageViewFragment.java | 11 +- src/com/fsck/k9/helper/MessageHelper.java | 16 +- src/com/fsck/k9/mail/Folder.java | 30 +-- src/com/fsck/k9/mail/Message.java | 13 +- src/com/fsck/k9/mail/Store.java | 209 +----------------- .../k9/mail/store/ImapResponseParser.java | 2 +- src/com/fsck/k9/mail/store/ImapStore.java | 74 +++---- src/com/fsck/k9/mail/store/Pop3Store.java | 40 ++-- src/com/fsck/k9/mail/store/RemoteStore.java | 122 ++++++++++ src/com/fsck/k9/mail/store/StoreConfig.java | 32 +++ src/com/fsck/k9/mail/store/WebDavStore.java | 41 ++-- .../fsck/k9/mail/store/local/LocalFolder.java | 67 ++++-- .../k9/mail/store/local/LocalMessage.java | 43 +++- .../fsck/k9/mail/store/local/LocalStore.java | 82 ++++++- .../fsck/k9/mail/transport/SmtpTransport.java | 3 +- .../k9/mail/transport/WebDavTransport.java | 10 +- .../fsck/k9/preferences/SettingsExporter.java | 4 +- .../fsck/k9/preferences/SettingsImporter.java | 4 +- .../fsck/k9/provider/AttachmentProvider.java | 4 +- src/com/fsck/k9/provider/MessageProvider.java | 21 +- .../k9/service/NotificationActionService.java | 8 +- .../fsck/k9/mail/store/ImapStoreUriTest.java | 21 +- 36 files changed, 641 insertions(+), 589 deletions(-) create mode 100644 src/com/fsck/k9/mail/store/RemoteStore.java create mode 100644 src/com/fsck/k9/mail/store/StoreConfig.java diff --git a/src/com/fsck/k9/Account.java b/src/com/fsck/k9/Account.java index 54a04197a..d7413b6d1 100644 --- a/src/com/fsck/k9/Account.java +++ b/src/com/fsck/k9/Account.java @@ -29,6 +29,8 @@ import com.fsck.k9.mail.Address; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Store; import com.fsck.k9.mail.Folder.FolderClass; +import com.fsck.k9.mail.store.RemoteStore; +import com.fsck.k9.mail.store.StoreConfig; import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.mail.store.StorageManager.StorageProvider; import com.fsck.k9.mail.store.local.LocalStore; @@ -48,7 +50,7 @@ import com.larswerkman.colorpicker.ColorPicker; * Account stores all of the settings for a single account defined by the user. It is able to save * and delete itself given a Preferences to work with. Each account is defined by a UUID. */ -public class Account implements BaseAccount { +public class Account implements BaseAccount, StoreConfig { /** * Default value for the inbox folder (never changes for POP3 and IMAP) */ @@ -1289,11 +1291,11 @@ public class Account implements BaseAccount { } public LocalStore getLocalStore() throws MessagingException { - return Store.getLocalInstance(this, K9.app); + return LocalStore.getInstance(this, K9.app); } public Store getRemoteStore() throws MessagingException { - return Store.getRemoteInstance(this); + return RemoteStore.getInstance(this); } // It'd be great if this actually went into the store implementation diff --git a/src/com/fsck/k9/Preferences.java b/src/com/fsck/k9/Preferences.java index 5fd4929a3..6504626a2 100644 --- a/src/com/fsck/k9/Preferences.java +++ b/src/com/fsck/k9/Preferences.java @@ -14,6 +14,7 @@ import android.content.SharedPreferences; import android.util.Log; import com.fsck.k9.mail.Store; +import com.fsck.k9.mail.store.local.LocalStore; import com.fsck.k9.preferences.Editor; import com.fsck.k9.preferences.Storage; @@ -121,7 +122,7 @@ public class Preferences { accountsInOrder.remove(account); } - Store.removeAccount(account); + LocalStore.removeAccount(account); account.deleteCertificates(); account.delete(this); diff --git a/src/com/fsck/k9/activity/Accounts.java b/src/com/fsck/k9/activity/Accounts.java index 738ee32a3..5b7a757ae 100644 --- a/src/com/fsck/k9/activity/Accounts.java +++ b/src/com/fsck/k9/activity/Accounts.java @@ -5,7 +5,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.EnumSet; import java.util.HashSet; @@ -76,12 +75,11 @@ import com.fsck.k9.activity.setup.Prefs; import com.fsck.k9.activity.setup.WelcomeMessage; import com.fsck.k9.controller.MessagingController; import com.fsck.k9.helper.SizeFormatter; -import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.AuthType; import com.fsck.k9.mail.ServerSettings; -import com.fsck.k9.mail.Store; import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.internet.MimeUtility; +import com.fsck.k9.mail.store.RemoteStore; import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.mail.store.WebDavStore; import com.fsck.k9.preferences.SettingsExporter; @@ -771,7 +769,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener { } private void show(final Accounts activity, boolean restore) { - ServerSettings incoming = Store.decodeStoreUri(mAccount.getStoreUri()); + ServerSettings incoming = RemoteStore.decodeStoreUri(mAccount.getStoreUri()); ServerSettings outgoing = Transport.decodeTransportUri(mAccount.getTransportUri()); /* @@ -992,9 +990,9 @@ public class Accounts extends K9ListActivity implements OnItemClickListener { if (mIncomingPassword != null) { // Set incoming server password String storeUri = mAccount.getStoreUri(); - ServerSettings incoming = Store.decodeStoreUri(storeUri); + ServerSettings incoming = RemoteStore.decodeStoreUri(storeUri); ServerSettings newIncoming = incoming.newPassword(mIncomingPassword); - String newStoreUri = Store.createStoreUri(newIncoming); + String newStoreUri = RemoteStore.createStoreUri(newIncoming); mAccount.setStoreUri(newStoreUri); } diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index 141399bcc..2e6bacf99 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -108,6 +108,7 @@ import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mail.internet.TextBodyBuilder; import com.fsck.k9.mail.store.local.LocalAttachmentBody; +import com.fsck.k9.mail.store.local.LocalMessage; import com.fsck.k9.mail.store.local.TempFileBody; import com.fsck.k9.mail.store.local.TempFileMessageBody; import com.fsck.k9.view.MessageWebView; @@ -435,17 +436,15 @@ public class MessageCompose extends K9Activity implements OnClickListener, * Get intent for composing a new message as a reply to the given message. If replyAll is true * the function is reply all instead of simply reply. * @param context - * @param account * @param message * @param replyAll * @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message */ public static Intent getActionReplyIntent( - Context context, - Account account, - Message message, - boolean replyAll, - String messageBody) { + Context context, + Message message, + boolean replyAll, + String messageBody) { Intent i = new Intent(context, MessageCompose.class); i.putExtra(EXTRA_MESSAGE_BODY, messageBody); i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference()); @@ -468,25 +467,22 @@ public class MessageCompose extends K9Activity implements OnClickListener, */ public static void actionReply( Context context, - Account account, Message message, boolean replyAll, String messageBody) { - context.startActivity(getActionReplyIntent(context, account, message, replyAll, messageBody)); + context.startActivity(getActionReplyIntent(context, message, replyAll, messageBody)); } /** * Compose a new message as a forward of the given message. * @param context - * @param account * @param message * @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message */ public static void actionForward( - Context context, - Account account, - Message message, - String messageBody) { + Context context, + Message message, + String messageBody) { Intent i = new Intent(context, MessageCompose.class); i.putExtra(EXTRA_MESSAGE_BODY, messageBody); i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference()); @@ -2646,7 +2642,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, * @param message * The source message used to populate the various text fields. */ - private void processSourceMessage(Message message) { + private void processSourceMessage(LocalMessage message) { try { switch (mAction) { case REPLY: @@ -2800,7 +2796,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, } } - private void processDraftMessage(Message message) throws MessagingException { + private void processDraftMessage(LocalMessage message) throws MessagingException { String showQuotedTextMode = "NONE"; mDraftId = MessagingController.getInstance(getApplication()).getId(message); @@ -2852,7 +2848,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, newIdentity.setSignature(k9identity.get(IdentityField.SIGNATURE)); mSignatureChanged = true; } else { - newIdentity.setSignatureUse(message.getFolder().getAccount().getSignatureUse()); + newIdentity.setSignatureUse(message.getFolder().getSignatureUse()); newIdentity.setSignature(mIdentity.getSignature()); } @@ -3437,7 +3433,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, } updateMessageFormat(); } else { - processSourceMessage(message); + processSourceMessage((LocalMessage) message); mSourceProcessed = true; } } diff --git a/src/com/fsck/k9/activity/MessageInfoHolder.java b/src/com/fsck/k9/activity/MessageInfoHolder.java index eecf4f80d..e217a0f78 100644 --- a/src/com/fsck/k9/activity/MessageInfoHolder.java +++ b/src/com/fsck/k9/activity/MessageInfoHolder.java @@ -2,6 +2,7 @@ package com.fsck.k9.activity; import java.util.Date; import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.store.local.LocalMessage; public class MessageInfoHolder { public String date; @@ -18,7 +19,7 @@ public class MessageInfoHolder { public boolean forwarded; public boolean flagged; public boolean dirty; - public Message message; + public LocalMessage message; public FolderInfoHolder folder; public boolean selected; public String account; @@ -31,7 +32,7 @@ public class MessageInfoHolder { @Override public boolean equals(Object o) { - if (o instanceof MessageInfoHolder == false) { + if (!(o instanceof MessageInfoHolder)) { return false; } MessageInfoHolder other = (MessageInfoHolder)o; diff --git a/src/com/fsck/k9/activity/MessageList.java b/src/com/fsck/k9/activity/MessageList.java index 25306140f..765d03673 100644 --- a/src/com/fsck/k9/activity/MessageList.java +++ b/src/com/fsck/k9/activity/MessageList.java @@ -1199,17 +1199,17 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme @Override public void onForward(Message message) { - MessageCompose.actionForward(this, message.getFolder().getAccount(), message, null); + MessageCompose.actionForward(this, message, null); } @Override public void onReply(Message message) { - MessageCompose.actionReply(this, message.getFolder().getAccount(), message, false, null); + MessageCompose.actionReply(this, message, false, null); } @Override public void onReplyAll(Message message) { - MessageCompose.actionReply(this, message.getFolder().getAccount(), message, true, null); + MessageCompose.actionReply(this, message, true, null); } @Override @@ -1400,17 +1400,17 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme @Override public void onReply(Message message, PgpData pgpData) { - MessageCompose.actionReply(this, mAccount, message, false, pgpData.getDecryptedData()); + MessageCompose.actionReply(this, message, false, pgpData.getDecryptedData()); } @Override public void onReplyAll(Message message, PgpData pgpData) { - MessageCompose.actionReply(this, mAccount, message, true, pgpData.getDecryptedData()); + MessageCompose.actionReply(this, message, true, pgpData.getDecryptedData()); } @Override public void onForward(Message mMessage, PgpData mPgpData) { - MessageCompose.actionForward(this, mAccount, mMessage, mPgpData.getDecryptedData()); + MessageCompose.actionForward(this, mMessage, mPgpData.getDecryptedData()); } @Override diff --git a/src/com/fsck/k9/activity/MessageReference.java b/src/com/fsck/k9/activity/MessageReference.java index 990d99774..bb2d6264d 100644 --- a/src/com/fsck/k9/activity/MessageReference.java +++ b/src/com/fsck/k9/activity/MessageReference.java @@ -13,6 +13,8 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.store.local.LocalFolder; +import com.fsck.k9.mail.store.local.LocalMessage; import java.util.StringTokenizer; @@ -128,13 +130,13 @@ public class MessageReference implements Parcelable { '}'; } - public Message restoreToLocalMessage(Context context) { + public LocalMessage restoreToLocalMessage(Context context) { try { Account account = Preferences.getPreferences(context).getAccount(accountUuid); if (account != null) { - Folder folder = account.getLocalStore().getFolder(folderName); + LocalFolder folder = account.getLocalStore().getFolder(folderName); if (folder != null) { - Message message = folder.getMessage(uid); + LocalMessage message = folder.getMessage(uid); if (message != null) { return message; } else { diff --git a/src/com/fsck/k9/activity/setup/AccountSetupBasics.java b/src/com/fsck/k9/activity/setup/AccountSetupBasics.java index 56d409743..eab3db5fd 100644 --- a/src/com/fsck/k9/activity/setup/AccountSetupBasics.java +++ b/src/com/fsck/k9/activity/setup/AccountSetupBasics.java @@ -38,9 +38,9 @@ import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.AuthType; import com.fsck.k9.mail.ConnectionSecurity; import com.fsck.k9.mail.ServerSettings; -import com.fsck.k9.mail.Store; import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.store.ImapStore; +import com.fsck.k9.mail.store.RemoteStore; import com.fsck.k9.mail.transport.SmtpTransport; import com.fsck.k9.view.ClientCertificateSpinner; import com.fsck.k9.view.ClientCertificateSpinner.OnClientCertificateChangedListener; @@ -421,7 +421,7 @@ public class AccountSetupBasics extends K9Activity ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias); ServerSettings transportServer = new ServerSettings(SmtpTransport.TRANSPORT_TYPE, "mail." + domain, -1, ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias); - String storeUri = Store.createStoreUri(storeServer); + String storeUri = RemoteStore.createStoreUri(storeServer); String transportUri = Transport.createTransportUri(transportServer); mAccount.setStoreUri(storeUri); mAccount.setTransportUri(transportUri); diff --git a/src/com/fsck/k9/activity/setup/AccountSetupIncoming.java b/src/com/fsck/k9/activity/setup/AccountSetupIncoming.java index ff4cd531b..5d417309f 100644 --- a/src/com/fsck/k9/activity/setup/AccountSetupIncoming.java +++ b/src/com/fsck/k9/activity/setup/AccountSetupIncoming.java @@ -27,6 +27,7 @@ import com.fsck.k9.mail.Store; import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.store.ImapStore; import com.fsck.k9.mail.store.Pop3Store; +import com.fsck.k9.mail.store.RemoteStore; import com.fsck.k9.mail.store.WebDavStore; import com.fsck.k9.mail.store.ImapStore.ImapStoreSettings; import com.fsck.k9.mail.store.WebDavStore.WebDavStoreSettings; @@ -163,7 +164,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener } try { - ServerSettings settings = Store.decodeStoreUri(mAccount.getStoreUri()); + ServerSettings settings = RemoteStore.decodeStoreUri(mAccount.getStoreUri()); if (savedInstanceState == null) { // The first item is selected if settings.authenticationType is null or is not in mAuthTypeAdapter @@ -610,7 +611,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener ServerSettings settings = new ServerSettings(mStoreType, host, port, connectionSecurity, authType, username, password, clientCertificateAlias, extra); - mAccount.setStoreUri(Store.createStoreUri(settings)); + mAccount.setStoreUri(RemoteStore.createStoreUri(settings)); mAccount.setCompression(Account.TYPE_MOBILE, mCompressionMobile.isChecked()); mAccount.setCompression(Account.TYPE_WIFI, mCompressionWifi.isChecked()); diff --git a/src/com/fsck/k9/cache/EmailProviderCache.java b/src/com/fsck/k9/cache/EmailProviderCache.java index 9b3e7d5e0..ccd712d12 100644 --- a/src/com/fsck/k9/cache/EmailProviderCache.java +++ b/src/com/fsck/k9/cache/EmailProviderCache.java @@ -123,13 +123,11 @@ public class EmailProviderCache { } } - public void hideMessages(List messages) { + public void hideMessages(List messages) { synchronized (mHiddenMessageCache) { - for (Message message : messages) { - LocalMessage localMessage = (LocalMessage) message; - long messageId = localMessage.getId(); - long folderId = ((LocalFolder) localMessage.getFolder()).getId(); - mHiddenMessageCache.put(messageId, folderId); + for (LocalMessage message : messages) { + long messageId = message.getId(); + mHiddenMessageCache.put(messageId, message.getFolder().getId()); } } diff --git a/src/com/fsck/k9/controller/MessageRetrievalListener.java b/src/com/fsck/k9/controller/MessageRetrievalListener.java index 24ecb52e2..6af8714af 100644 --- a/src/com/fsck/k9/controller/MessageRetrievalListener.java +++ b/src/com/fsck/k9/controller/MessageRetrievalListener.java @@ -3,10 +3,10 @@ package com.fsck.k9.controller; import com.fsck.k9.mail.Message; -public interface MessageRetrievalListener { +public interface MessageRetrievalListener { public void messageStarted(String uid, int number, int ofTotal); - public void messageFinished(Message message, int number, int ofTotal); + public void messageFinished(T message, int number, int ofTotal); /** * FIXME this method is almost never invoked by various Stores! Don't rely on it unless fixed!! diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index 7f889650b..d776c5fb3 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -312,7 +312,7 @@ public class MessagingController implements Runnable { private static final Set SYNC_FLAGS = EnumSet.of(Flag.SEEN, Flag.FLAGGED, Flag.ANSWERED, Flag.FORWARDED); - private void suppressMessages(Account account, List messages) { + private void suppressMessages(Account account, List messages) { EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(), mApplication.getApplicationContext()); cache.hideMessages(messages); @@ -324,13 +324,11 @@ public class MessagingController implements Runnable { cache.unhideMessages(messages); } - private boolean isMessageSuppressed(Account account, Message message) { - LocalMessage localMessage = (LocalMessage) message; - String accountUuid = account.getUuid(); - long messageId = localMessage.getId(); - long folderId = ((LocalFolder) localMessage.getFolder()).getId(); + private boolean isMessageSuppressed(LocalMessage message) { + long messageId = message.getId(); + long folderId = message.getFolder().getId(); - EmailProviderCache cache = EmailProviderCache.getCache(accountUuid, + EmailProviderCache cache = EmailProviderCache.getCache(message.getFolder().getUuid(), mApplication.getApplicationContext()); return cache.isMessageHidden(messageId, folderId); } @@ -690,15 +688,15 @@ public class MessagingController implements Runnable { } // Collecting statistics of the search result - MessageRetrievalListener retrievalListener = new MessageRetrievalListener() { + MessageRetrievalListener retrievalListener = new MessageRetrievalListener() { @Override public void messageStarted(String message, int number, int ofTotal) {} @Override public void messagesFinished(int number) {} @Override - public void messageFinished(Message message, int number, int ofTotal) { - if (!isMessageSuppressed(message.getFolder().getAccount(), message)) { - List messages = new ArrayList(); + public void messageFinished(LocalMessage message, int number, int ofTotal) { + if (!isMessageSuppressed(message)) { + List messages = new ArrayList(); messages.add(message); stats.unreadMessageCount += (!message.isSet(Flag.SEEN)) ? 1 : 0; @@ -762,7 +760,7 @@ public class MessagingController implements Runnable { final Account acct = Preferences.getPreferences(mApplication.getApplicationContext()).getAccount(acctUuid); if (listener != null) { - listener.remoteSearchStarted(acct, folderName); + listener.remoteSearchStarted(folderName); } List extraResults = new ArrayList(); @@ -791,7 +789,7 @@ public class MessagingController implements Runnable { messages.clear(); if (listener != null) { - listener.remoteSearchServerQueryComplete(acct, folderName, remoteMessages.size()); + listener.remoteSearchServerQueryComplete(folderName, remoteMessages.size(), acct.getRemoteSearchNumResults()); } Collections.sort(remoteMessages, new UidReverseComparator()); @@ -811,13 +809,13 @@ public class MessagingController implements Runnable { } else { Log.e(K9.LOG_TAG, "Could not complete remote search", e); if (listener != null) { - listener.remoteSearchFailed(acct, null, e.getMessage()); + listener.remoteSearchFailed(null, e.getMessage()); } addErrorMessage(acct, null, e); } } finally { if (listener != null) { - listener.remoteSearchFinished(acct, folderName, 0, extraResults); + listener.remoteSearchFinished(folderName, 0, acct.getRemoteSearchNumResults(), extraResults); } } @@ -878,7 +876,7 @@ public class MessagingController implements Runnable { } if (listener != null) { - listener.remoteSearchAddMessage(remoteFolder.getAccount(), remoteFolder.getName(), localMsg, i, messages.size()); + listener.remoteSearchAddMessage(remoteFolder.getName(), localMsg, i, messages.size()); } } } @@ -1455,7 +1453,7 @@ public class MessagingController implements Runnable { } } - private void fetchUnsyncedMessages(final Account account, final Folder remoteFolder, + private void fetchUnsyncedMessages(final Account account, final Folder remoteFolder, final LocalFolder localFolder, List unsyncedMessages, final List smallMessages, @@ -1473,9 +1471,9 @@ public class MessagingController implements Runnable { final List chunk = new ArrayList(UNSYNC_CHUNK_SIZE); remoteFolder.fetch(unsyncedMessages, fp, - new MessageRetrievalListener() { + new MessageRetrievalListener() { @Override - public void messageFinished(Message message, int number, int ofTotal) { + public void messageFinished(T message, int number, int ofTotal) { try { String newPushState = remoteFolder.getNewPushState(localFolder.getPushState(), message); if (newPushState != null) { @@ -1564,7 +1562,7 @@ public class MessagingController implements Runnable { localFolder.appendMessages(messages); for (final Message message : messages) { - final Message localMessage = localFolder.getMessage(message.getUid()); + final LocalMessage localMessage = localFolder.getMessage(message.getUid()); syncFlags(localMessage, message); if (K9.DEBUG) Log.v(K9.LOG_TAG, "About to notify listeners that we got a new unsynced message " @@ -1592,9 +1590,9 @@ public class MessagingController implements Runnable { return true; } - private void downloadSmallMessages(final Account account, final Folder remoteFolder, + private void downloadSmallMessages(final Account account, final Folder remoteFolder, final LocalFolder localFolder, - List smallMessages, + List smallMessages, final AtomicInteger progress, final int unreadBeforeStart, final AtomicInteger newMessages, @@ -1608,9 +1606,9 @@ public class MessagingController implements Runnable { Log.d(K9.LOG_TAG, "SYNC: Fetching small messages for folder " + folder); remoteFolder.fetch(smallMessages, - fp, new MessageRetrievalListener() { + fp, new MessageRetrievalListener() { @Override - public void messageFinished(final Message message, int number, int ofTotal) { + public void messageFinished(final T message, int number, int ofTotal) { try { if (!shouldImportMessage(account, folder, message, progress, earliestDate)) { @@ -1671,9 +1669,9 @@ public class MessagingController implements Runnable { - private void downloadLargeMessages(final Account account, final Folder remoteFolder, + private void downloadLargeMessages(final Account account, final Folder remoteFolder, final LocalFolder localFolder, - List largeMessages, + List largeMessages, final AtomicInteger progress, final int unreadBeforeStart, final AtomicInteger newMessages, @@ -1821,11 +1819,11 @@ public class MessagingController implements Runnable { remoteFolder.fetch(undeletedMessages, fp, null); for (Message remoteMessage : syncFlagMessages) { - Message localMessage = localFolder.getMessage(remoteMessage.getUid()); + LocalMessage localMessage = localFolder.getMessage(remoteMessage.getUid()); boolean messageChanged = syncFlags(localMessage, remoteMessage); if (messageChanged) { boolean shouldBeNotifiedOf = false; - if (localMessage.isSet(Flag.DELETED) || isMessageSuppressed(account, localMessage)) { + if (localMessage.isSet(Flag.DELETED) || isMessageSuppressed(localMessage)) { for (MessagingListener l : getListeners()) { l.synchronizeMailboxRemovedMessage(account, folder, localMessage); } @@ -1859,13 +1857,13 @@ public class MessagingController implements Runnable { } } - private boolean syncFlags(Message localMessage, Message remoteMessage) throws MessagingException { + private boolean syncFlags(LocalMessage localMessage, Message remoteMessage) throws MessagingException { boolean messageChanged = false; if (localMessage == null || localMessage.isSet(Flag.DELETED)) { return false; } if (remoteMessage.isSet(Flag.DELETED)) { - if (localMessage.getFolder().getAccount().syncRemoteDeletions()) { + if (localMessage.getFolder().syncRemoteDeletions()) { localMessage.setFlag(Flag.DELETED, true); messageChanged = true; } @@ -2857,7 +2855,7 @@ public class MessagingController implements Runnable { * @param newState * {@code true}, if the flag should be set. {@code false} if it should be removed. */ - public void setFlag(Account account, String folderName, List messages, Flag flag, + public void setFlag(Account account, String folderName, List messages, Flag flag, boolean newState) { // TODO: Put this into the background, but right now some callers depend on the message // objects being modified right after this method returns. @@ -3775,7 +3773,7 @@ public class MessagingController implements Runnable { } } public void moveMessages(final Account account, final String srcFolder, - final List messages, final String destFolder, + final List messages, final String destFolder, final MessagingListener listener) { suppressMessages(account, messages); @@ -3790,7 +3788,7 @@ public class MessagingController implements Runnable { } public void moveMessagesInThread(final Account account, final String srcFolder, - final List messages, final String destFolder) { + final List messages, final String destFolder) { suppressMessages(account, messages); @@ -3808,14 +3806,14 @@ public class MessagingController implements Runnable { }); } - public void moveMessage(final Account account, final String srcFolder, final Message message, + public void moveMessage(final Account account, final String srcFolder, final LocalMessage message, final String destFolder, final MessagingListener listener) { moveMessages(account, srcFolder, Collections.singletonList(message), destFolder, listener); } public void copyMessages(final Account account, final String srcFolder, - final List messages, final String destFolder, + final List messages, final String destFolder, final MessagingListener listener) { putBackground("copyMessages", null, new Runnable() { @@ -3828,7 +3826,7 @@ public class MessagingController implements Runnable { } public void copyMessagesInThread(final Account account, final String srcFolder, - final List messages, final String destFolder) { + final List messages, final String destFolder) { putBackground("copyMessagesInThread", null, new Runnable() { @Override @@ -3851,7 +3849,7 @@ public class MessagingController implements Runnable { } private void moveOrCopyMessageSynchronous(final Account account, final String srcFolder, - final List inMessages, final String destFolder, final boolean isCopy, + final List inMessages, final String destFolder, final boolean isCopy, MessagingListener listener) { try { @@ -3962,7 +3960,7 @@ public class MessagingController implements Runnable { localFolder.open(Folder.OPEN_MODE_RW); String uid = localFolder.getMessageUidById(id); if (uid != null) { - Message message = localFolder.getMessage(uid); + LocalMessage message = localFolder.getMessage(uid); if (message != null) { deleteMessages(Collections.singletonList(message), null); } @@ -3974,7 +3972,7 @@ public class MessagingController implements Runnable { } } - public void deleteThreads(final List messages) { + public void deleteThreads(final List messages) { actOnMessages(messages, new MessageActor() { @Override @@ -4006,7 +4004,7 @@ public class MessagingController implements Runnable { } } - public List collectMessagesInThreads(Account account, List messages) + public List collectMessagesInThreads(Account account, List messages) throws MessagingException { LocalStore localStore = account.getLocalStore(); @@ -4025,7 +4023,7 @@ public class MessagingController implements Runnable { return messagesInThreads; } - public void deleteMessages(final List messages, final MessagingListener listener) { + public void deleteMessages(final List messages, final MessagingListener listener) { actOnMessages(messages, new MessageActor() { @Override @@ -5685,15 +5683,15 @@ public class MessagingController implements Runnable { } - private void actOnMessages(List messages, MessageActor actor) { + private void actOnMessages(List messages, MessageActor actor) { Map>> accountMap = new HashMap>>(); - for (Message message : messages) { + for (LocalMessage message : messages) { if ( message == null) { continue; } Folder folder = message.getFolder(); - Account account = folder.getAccount(); + Account account = message.getAccount(); Map> folderMap = accountMap.get(account); if (folderMap == null) { diff --git a/src/com/fsck/k9/controller/MessagingListener.java b/src/com/fsck/k9/controller/MessagingListener.java index 5c5a66f50..470cc6387 100644 --- a/src/com/fsck/k9/controller/MessagingListener.java +++ b/src/com/fsck/k9/controller/MessagingListener.java @@ -11,6 +11,7 @@ import com.fsck.k9.BaseAccount; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Part; +import com.fsck.k9.mail.store.local.LocalMessage; /** * Defines the interface that {@link MessagingController} will use to callback to requesters. @@ -42,10 +43,8 @@ public class MessagingListener { public void listLocalMessagesStarted(Account account, String folder) {} - public void listLocalMessages(Account account, String folder, Message[] messages) {} - public void listLocalMessagesAddMessages(Account account, String folder, - List messages) {} + List messages) {} public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {} @@ -156,10 +155,9 @@ public class MessagingListener { /** * Called when a remote search is started * - * @param acct * @param folder */ - public void remoteSearchStarted(Account acct, String folder) {} + public void remoteSearchStarted(String folder) {} /** @@ -167,35 +165,30 @@ public class MessagingListener { * * @param numResults */ - public void remoteSearchServerQueryComplete(Account account, String folderName, int numResults) { } + public void remoteSearchServerQueryComplete(String folderName, int numResults, int maxResults) { } /** * Called when a new result message is available for a remote search * Can assume headers have been downloaded, but potentially not body. - * @param account * @param folder * @param message */ - public void remoteSearchAddMessage(Account account, String folder, Message message, int numDone, int numTotal) { } + public void remoteSearchAddMessage(String folder, Message message, int numDone, int numTotal) { } /** * Called when Remote Search is fully complete - * - * @param acct - * @param folder + * @param folder * @param numResults */ - public void remoteSearchFinished(Account acct, String folder, int numResults, List extraResults) {} + public void remoteSearchFinished(String folder, int numResults, int maxResults, List extraResults) {} /** * Called when there was a problem with a remote search operation. - * - * @param acct - * @param folder + * @param folder * @param err */ - public void remoteSearchFailed(Account acct, String folder, String err) { } + public void remoteSearchFailed(String folder, String err) { } /** * General notification messages subclasses can override to be notified that the controller diff --git a/src/com/fsck/k9/fragment/MessageListFragment.java b/src/com/fsck/k9/fragment/MessageListFragment.java index 80b19b33a..48e5b6190 100644 --- a/src/com/fsck/k9/fragment/MessageListFragment.java +++ b/src/com/fsck/k9/fragment/MessageListFragment.java @@ -89,6 +89,7 @@ import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.store.local.LocalFolder; +import com.fsck.k9.mail.store.local.LocalMessage; import com.fsck.k9.mail.store.local.LocalStore; import com.fsck.k9.provider.EmailProvider; import com.fsck.k9.provider.EmailProvider.MessageColumns; @@ -427,7 +428,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener * Relevant messages for the current context when we have to remember the chosen messages * between user interactions (e.g. selecting a folder for move operation). */ - private List mActiveMessages; + private List mActiveMessages; /* package visibility for faster inner class access */ MessageHelper mMessageHelper; @@ -1035,7 +1036,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener return null; } - private Folder getFolderById(Account account, long folderId) { + private LocalFolder getFolderById(Account account, long folderId) { try { LocalStore localStore = account.getLocalStore(); LocalFolder localFolder = localStore.getFolderById(folderId); @@ -1309,11 +1310,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener changeSort(sorts[curIndex]); } - private void onDelete(Message message) { + private void onDelete(LocalMessage message) { onDelete(Collections.singletonList(message)); } - private void onDelete(List messages) { + private void onDelete(List messages) { if (K9.confirmDelete()) { // remember the message selection for #onCreateDialog(int) mActiveMessages = messages; @@ -1323,7 +1324,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener } } - private void onDeleteConfirmed(List messages) { + private void onDeleteConfirmed(List messages) { if (mThreadedList) { mController.deleteThreads(messages); } else { @@ -1345,15 +1346,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener } final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER); - final List messages = mActiveMessages; + final List messages = mActiveMessages; if (destFolderName != null) { mActiveMessages = null; // don't need it any more if (messages.size() > 0) { - Account account = messages.get(0).getFolder().getAccount(); - account.setLastSelectedFolderName(destFolderName); + messages.get(0).getFolder().setLastSelectedFolderName(destFolderName); } switch (requestCode) { @@ -1539,7 +1539,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener break; } case R.id.delete: { - Message message = getMessageAtPosition(adapterPosition); + LocalMessage message = getMessageAtPosition(adapterPosition); onDelete(message); break; } @@ -1562,23 +1562,19 @@ public class MessageListFragment extends Fragment implements OnItemClickListener // only if the account supports this case R.id.archive: { - Message message = getMessageAtPosition(adapterPosition); - onArchive(message); + onArchive(getMessageAtPosition(adapterPosition)); break; } case R.id.spam: { - Message message = getMessageAtPosition(adapterPosition); - onSpam(message); + onSpam(getMessageAtPosition(adapterPosition)); break; } case R.id.move: { - Message message = getMessageAtPosition(adapterPosition); - onMove(message); + onMove(getMessageAtPosition(adapterPosition)); break; } case R.id.copy: { - Message message = getMessageAtPosition(adapterPosition); - onCopy(message); + onCopy(getMessageAtPosition(adapterPosition)); break; } } @@ -1711,7 +1707,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener class MessageListActivityListener extends ActivityListener { @Override - public void remoteSearchFailed(Account acct, String folder, final String err) { + public void remoteSearchFailed(String folder, final String err) { mHandler.post(new Runnable() { @Override public void run() { @@ -1725,7 +1721,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener } @Override - public void remoteSearchStarted(Account acct, String folder) { + public void remoteSearchStarted(String folder) { mHandler.progress(true); mHandler.updateFooter(mContext.getString(R.string.remote_search_sending_query)); } @@ -1736,12 +1732,12 @@ public class MessageListFragment extends Fragment implements OnItemClickListener } @Override - public void remoteSearchFinished(Account acct, String folder, int numResults, List extraResults) { + public void remoteSearchFinished(String folder, int numResults, int maxResults, List extraResults) { mHandler.progress(false); mHandler.remoteSearchFinished(); mExtraSearchResults = extraResults; if (extraResults != null && extraResults.size() > 0) { - mHandler.updateFooter(String.format(mContext.getString(R.string.load_more_messages_fmt), acct.getRemoteSearchNumResults())); + mHandler.updateFooter(String.format(mContext.getString(R.string.load_more_messages_fmt), maxResults)); } else { mHandler.updateFooter(""); } @@ -1750,11 +1746,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener } @Override - public void remoteSearchServerQueryComplete(Account account, String folderName, int numResults) { + public void remoteSearchServerQueryComplete(String folderName, int numResults, int maxResults) { mHandler.progress(true); - if (account != null && account.getRemoteSearchNumResults() != 0 && numResults > account.getRemoteSearchNumResults()) { + if (maxResults != 0 && numResults > maxResults) { mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading_limited, - account.getRemoteSearchNumResults(), numResults)); + maxResults, numResults)); } else { mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading, numResults)); } @@ -2416,7 +2412,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener computeBatchDirection(); } - private void onMove(Message message) { + private void onMove(LocalMessage message) { onMove(Collections.singletonList(message)); } @@ -2426,7 +2422,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener * @param messages * Never {@code null}. */ - private void onMove(List messages) { + private void onMove(List messages) { if (!checkCopyOrMovePossible(messages, FolderOperation.MOVE)) { return; } @@ -2440,12 +2436,13 @@ public class MessageListFragment extends Fragment implements OnItemClickListener folder = null; } - Account account = messages.get(0).getFolder().getAccount(); - displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_MOVE, account, folder, messages); + displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_MOVE, folder, + messages.get(0).getFolder().getUuid(), null, + messages); } - private void onCopy(Message message) { + private void onCopy(LocalMessage message) { onCopy(Collections.singletonList(message)); } @@ -2455,7 +2452,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener * @param messages * Never {@code null}. */ - private void onCopy(List messages) { + private void onCopy(List messages) { if (!checkCopyOrMovePossible(messages, FolderOperation.COPY)) { return; } @@ -2469,7 +2466,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener folder = null; } - displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_COPY, mAccount, folder, messages); + displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_COPY, folder, + messages.get(0).getFolder().getUuid(), + null, + messages); } /** @@ -2486,12 +2486,13 @@ public class MessageListFragment extends Fragment implements OnItemClickListener * * @see #startActivityForResult(Intent, int) */ - private void displayFolderChoice(int requestCode, Account account, Folder folder, - List messages) { + private void displayFolderChoice(int requestCode, Folder folder, + String accountId, String lastSelectedFolderName, + List messages) { Intent intent = new Intent(getActivity(), ChooseFolder.class); - intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, account.getUuid()); - intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, account.getLastSelectedFolderName()); + intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, accountId); + intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, lastSelectedFolderName); if (folder == null) { intent.putExtra(ChooseFolder.EXTRA_SHOW_CURRENT, "yes"); @@ -2504,14 +2505,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener startActivityForResult(intent, requestCode); } - private void onArchive(final Message message) { + private void onArchive(final LocalMessage message) { onArchive(Collections.singletonList(message)); } - private void onArchive(final List messages) { - Map> messagesByAccount = groupMessagesByAccount(messages); + private void onArchive(final List messages) { + Map> messagesByAccount = groupMessagesByAccount(messages); - for (Entry> entry : messagesByAccount.entrySet()) { + for (Entry> entry : messagesByAccount.entrySet()) { Account account = entry.getKey(); String archiveFolder = account.getArchiveFolderName(); @@ -2521,14 +2522,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener } } - private Map> groupMessagesByAccount(final List messages) { - Map> messagesByAccount = new HashMap>(); - for (Message message : messages) { - Account account = message.getFolder().getAccount(); + private Map> groupMessagesByAccount(final List messages) { + Map> messagesByAccount = new HashMap>(); + for (LocalMessage message : messages) { + Account account = message.getAccount(); - List msgList = messagesByAccount.get(account); + List msgList = messagesByAccount.get(account); if (msgList == null) { - msgList = new ArrayList(); + msgList = new ArrayList(); messagesByAccount.put(account, msgList); } @@ -2537,7 +2538,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener return messagesByAccount; } - private void onSpam(Message message) { + private void onSpam(LocalMessage message) { onSpam(Collections.singletonList(message)); } @@ -2547,7 +2548,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener * @param messages * The messages to move to the spam folder. Never {@code null}. */ - private void onSpam(List messages) { + private void onSpam(List messages) { if (K9.confirmSpam()) { // remember the message selection for #onCreateDialog(int) mActiveMessages = messages; @@ -2557,10 +2558,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener } } - private void onSpamConfirmed(List messages) { - Map> messagesByAccount = groupMessagesByAccount(messages); + private void onSpamConfirmed(List messages) { + Map> messagesByAccount = groupMessagesByAccount(messages); - for (Entry> entry : messagesByAccount.entrySet()) { + for (Entry> entry : messagesByAccount.entrySet()) { Account account = entry.getKey(); String spamFolder = account.getSpamFolderName(); @@ -2584,7 +2585,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener * * @return {@code true}, if operation is possible. */ - private boolean checkCopyOrMovePossible(final List messages, + private boolean checkCopyOrMovePossible(final List messages, final FolderOperation operation) { if (messages.isEmpty()) { @@ -2592,11 +2593,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener } boolean first = true; - for (final Message message : messages) { + for (final LocalMessage message : messages) { if (first) { first = false; // account check - final Account account = message.getFolder().getAccount(); + final Account account = message.getAccount(); if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(account)) || (operation == FolderOperation.COPY && !mController.isCopyCapable(account))) { return false; @@ -2622,7 +2623,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener * @param destination * The name of the destination folder. Never {@code null}. */ - private void copy(List messages, final String destination) { + private void copy(List messages, final String destination) { copyOrMove(messages, destination, FolderOperation.COPY); } @@ -2634,7 +2635,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener * @param destination * The name of the destination folder. Never {@code null}. */ - private void move(List messages, final String destination) { + private void move(List messages, final String destination) { copyOrMove(messages, destination, FolderOperation.MOVE); } @@ -2650,12 +2651,12 @@ public class MessageListFragment extends Fragment implements OnItemClickListener * @param operation * Specifies what operation to perform. Never {@code null}. */ - private void copyOrMove(List messages, final String destination, + private void copyOrMove(List messages, final String destination, final FolderOperation operation) { - Map> folderMap = new HashMap>(); + Map> folderMap = new HashMap>(); - for (Message message : messages) { + for (LocalMessage message : messages) { if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(message)) || (operation == FolderOperation.COPY && !mController.isCopyCapable(message))) { @@ -2674,19 +2675,19 @@ public class MessageListFragment extends Fragment implements OnItemClickListener continue; } - List outMessages = folderMap.get(folderName); + List outMessages = folderMap.get(folderName); if (outMessages == null) { - outMessages = new ArrayList(); + outMessages = new ArrayList(); folderMap.put(folderName, outMessages); } outMessages.add(message); } - for (Map.Entry> entry : folderMap.entrySet()) { + for (Map.Entry> entry : folderMap.entrySet()) { String folderName = entry.getKey(); - List outMessages = entry.getValue(); - Account account = outMessages.get(0).getFolder().getAccount(); + List outMessages = entry.getValue(); + Account account = outMessages.get(0).getAccount(); if (operation == FolderOperation.MOVE) { if (mThreadedList) { @@ -2859,7 +2860,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener */ switch (item.getItemId()) { case R.id.delete: { - List messages = getCheckedMessages(); + List messages = getCheckedMessages(); onDelete(messages); mSelectedCount = 0; break; @@ -2887,26 +2888,22 @@ public class MessageListFragment extends Fragment implements OnItemClickListener // only if the account supports this case R.id.archive: { - List messages = getCheckedMessages(); - onArchive(messages); + onArchive(getCheckedMessages()); mSelectedCount = 0; break; } case R.id.spam: { - List messages = getCheckedMessages(); - onSpam(messages); + onSpam(getCheckedMessages()); mSelectedCount = 0; break; } case R.id.move: { - List messages = getCheckedMessages(); - onMove(messages); + onMove(getCheckedMessages()); mSelectedCount = 0; break; } case R.id.copy: { - List messages = getCheckedMessages(); - onCopy(messages); + onCopy(getCheckedMessages()); mSelectedCount = 0; break; } @@ -2987,7 +2984,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener final Folder remoteFolder = mCurrentFolder.folder; remoteFolder.close(); // Send a remoteSearchFinished() message for good measure. - mListener.remoteSearchFinished(searchAccount, mCurrentFolder.name, 0, null); + mListener.remoteSearchFinished(mCurrentFolder.name, 0, searchAccount.getRemoteSearchNumResults(), null); } catch (Exception e) { // Since the user is going back, log and squash any exceptions. Log.e(K9.LOG_TAG, "Could not abort remote search before going back", e); @@ -3135,7 +3132,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener changeSort(mSortType); } - private Message getSelectedMessage() { + private LocalMessage getSelectedMessage() { int listViewPosition = mListView.getSelectedItemPosition(); int adapterPosition = listViewToAdapterPosition(listViewPosition); @@ -3158,7 +3155,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener return AdapterView.INVALID_POSITION; } - private Message getMessageAtPosition(int adapterPosition) { + private LocalMessage getMessageAtPosition(int adapterPosition) { if (adapterPosition == AdapterView.INVALID_POSITION) { return null; } @@ -3168,7 +3165,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener Account account = getAccountFromCursor(cursor); long folderId = cursor.getLong(FOLDER_ID_COLUMN); - Folder folder = getFolderById(account, folderId); + LocalFolder folder = getFolderById(account, folderId); try { return folder.getMessage(uid); @@ -3179,14 +3176,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener return null; } - private List getCheckedMessages() { - List messages = new ArrayList(mSelected.size()); + private List getCheckedMessages() { + List messages = new ArrayList(mSelected.size()); for (int position = 0, end = mAdapter.getCount(); position < end; position++) { Cursor cursor = (Cursor) mAdapter.getItem(position); long uniqueId = cursor.getLong(mUniqueIdColumn); if (mSelected.contains(uniqueId)) { - Message message = getMessageAtPosition(position); + LocalMessage message = getMessageAtPosition(position); if (message != null) { messages.add(message); } @@ -3197,7 +3194,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener } public void onDelete() { - Message message = getSelectedMessage(); + LocalMessage message = getSelectedMessage(); if (message != null) { onDelete(Collections.singletonList(message)); } @@ -3227,21 +3224,21 @@ public class MessageListFragment extends Fragment implements OnItemClickListener } public void onMove() { - Message message = getSelectedMessage(); + LocalMessage message = getSelectedMessage(); if (message != null) { onMove(message); } } public void onArchive() { - Message message = getSelectedMessage(); + LocalMessage message = getSelectedMessage(); if (message != null) { onArchive(message); } } public void onCopy() { - Message message = getSelectedMessage(); + LocalMessage message = getSelectedMessage(); if (message != null) { onCopy(message); } diff --git a/src/com/fsck/k9/fragment/MessageViewFragment.java b/src/com/fsck/k9/fragment/MessageViewFragment.java index 3a8bb4ddd..c8cfea928 100644 --- a/src/com/fsck/k9/fragment/MessageViewFragment.java +++ b/src/com/fsck/k9/fragment/MessageViewFragment.java @@ -75,7 +75,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener, private PgpData mPgpData; private Account mAccount; private MessageReference mMessageReference; - private Message mMessage; + private LocalMessage mMessage; private MessagingController mController; private Listener mListener = new Listener(); private MessageViewHandler mHandler = new MessageViewHandler(); @@ -311,7 +311,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener, // Disable the delete button after it's tapped (to try to prevent // accidental clicks) mFragmentListener.disableDeleteAction(); - Message messageToDelete = mMessage; + LocalMessage messageToDelete = mMessage; mFragmentListener.showNextMessageOrReturn(); mController.deleteMessages(Collections.singletonList(messageToDelete), null); } @@ -341,7 +341,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener, private void refileMessage(String dstFolder) { String srcFolder = mMessageReference.folderName; - Message messageToMove = mMessage; + LocalMessage messageToMove = mMessage; mFragmentListener.showNextMessageOrReturn(); mController.moveMessage(mAccount, srcFolder, messageToMove, dstFolder, null); } @@ -576,7 +576,8 @@ public class MessageViewFragment extends Fragment implements OnClickListener, @Override public void loadMessageForViewBodyAvailable(final Account account, String folder, String uid, final Message message) { - if (!mMessageReference.uid.equals(uid) || + if (!(message instanceof LocalMessage) || + !mMessageReference.uid.equals(uid) || !mMessageReference.folderName.equals(folder) || !mMessageReference.accountUuid.equals(account.getUuid())) { return; @@ -586,7 +587,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener, @Override public void run() { try { - mMessage = message; + mMessage = (LocalMessage) message; mMessageView.setMessage(account, (LocalMessage) message, mPgpData, mController, mListener); mFragmentListener.updateMenu(); diff --git a/src/com/fsck/k9/helper/MessageHelper.java b/src/com/fsck/k9/helper/MessageHelper.java index 26430b9e8..5df0161b8 100644 --- a/src/com/fsck/k9/helper/MessageHelper.java +++ b/src/com/fsck/k9/helper/MessageHelper.java @@ -14,6 +14,7 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Message.RecipientType; +import com.fsck.k9.mail.store.local.LocalMessage; public class MessageHelper { @@ -32,8 +33,10 @@ public class MessageHelper { mContext = context; } - public void populate(final MessageInfoHolder target, final Message message, - final FolderInfoHolder folder, final Account account) { + public void populate(final MessageInfoHolder target, + final LocalMessage message, + final FolderInfoHolder folder, + Account account) { final Contacts contactHelper = K9.showContactName() ? Contacts.getInstance(mContext) : null; try { target.message = message; @@ -68,14 +71,9 @@ public class MessageHelper { target.senderAddress = target.compareCounterparty; } - - - target.uid = message.getUid(); - - target.account = account.getUuid(); - target.uri = "email://messages/" + account.getAccountNumber() + "/" + message.getFolder().getName() + "/" + message.getUid(); - + target.account = message.getFolder().getUuid(); + target.uri = message.getUri(); } catch (MessagingException me) { Log.w(K9.LOG_TAG, "Unable to load message info", me); } diff --git a/src/com/fsck/k9/mail/Folder.java b/src/com/fsck/k9/mail/Folder.java index 88af1a31a..5669e9794 100644 --- a/src/com/fsck/k9/mail/Folder.java +++ b/src/com/fsck/k9/mail/Folder.java @@ -1,21 +1,17 @@ package com.fsck.k9.mail; -import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; import android.util.Log; -import com.fsck.k9.Account; + import com.fsck.k9.K9; import com.fsck.k9.Preferences; import com.fsck.k9.controller.MessageRetrievalListener; - -public abstract class Folder { - protected final Account mAccount; - +public abstract class Folder { private String status = null; private long lastChecked = 0; private long lastPush = 0; @@ -32,10 +28,6 @@ public abstract class Folder { HOLDS_FOLDERS, HOLDS_MESSAGES, } - protected Folder(Account account) { - mAccount = account; - } - /** * Forces an open of the MailProvider. If the provider is already open this * function returns without doing anything. @@ -83,7 +75,7 @@ public abstract class Folder { public abstract int getUnreadMessageCount() throws MessagingException; public abstract int getFlaggedMessageCount() throws MessagingException; - public abstract Message getMessage(String uid) throws MessagingException; + public abstract T getMessage(String uid) throws MessagingException; /** * Fetch the shells of messages between a range of UIDs and after a given date. @@ -94,7 +86,7 @@ public abstract class Folder { * @return List of messages * @throws MessagingException */ - public abstract List getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener) throws MessagingException; + public abstract List getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener) throws MessagingException; /** * Fetches the given list of messages. The specified listener is notified as @@ -104,13 +96,13 @@ public abstract class Folder { * @param listener Listener to notify as we download messages. * @return List of messages */ - public abstract List getMessages(MessageRetrievalListener listener) throws MessagingException; + public abstract List getMessages(MessageRetrievalListener listener) throws MessagingException; - public List getMessages(MessageRetrievalListener listener, boolean includeDeleted) throws MessagingException { + public List getMessages(MessageRetrievalListener listener, boolean includeDeleted) throws MessagingException { return getMessages(listener); } - public abstract List getMessages(String[] uids, MessageRetrievalListener listener) + public abstract List getMessages(String[] uids, MessageRetrievalListener listener) throws MessagingException; public abstract Map appendMessages(List messages) throws MessagingException; @@ -149,10 +141,10 @@ public abstract class Folder { * @throws MessagingException */ public abstract void fetch(List messages, FetchProfile fp, - MessageRetrievalListener listener) throws MessagingException; + MessageRetrievalListener listener) throws MessagingException; public void fetchPart(Message message, Part part, - MessageRetrievalListener listener) throws MessagingException { + MessageRetrievalListener listener) throws MessagingException { // This is causing trouble. Disabled for now. See issue 1733 //throw new RuntimeException("fetchPart() not implemented."); @@ -241,10 +233,6 @@ public abstract class Folder { this.status = status; } - public Account getAccount() { - return mAccount; - } - public List search(String queryString, final Set requiredFlags, final Set forbiddenFlags) throws MessagingException { throw new MessagingException("K-9 does not support searches on this folder type"); diff --git a/src/com/fsck/k9/mail/Message.java b/src/com/fsck/k9/mail/Message.java index 5a4d7a34a..452d312df 100644 --- a/src/com/fsck/k9/mail/Message.java +++ b/src/com/fsck/k9/mail/Message.java @@ -19,8 +19,8 @@ import com.fsck.k9.mail.store.UnavailableStorageException; public abstract class Message implements Part, CompositeBody { + protected MessageReference mReference; - private MessageReference mReference = null; public enum RecipientType { TO, CC, BCC, @@ -55,8 +55,7 @@ public abstract class Message implements Part, CompositeBody { } Message other = (Message)o; return (mUid.equals(other.getUid()) - && mFolder.getName().equals(other.getFolder().getName()) - && mFolder.getAccount().getUuid().equals(other.getFolder().getAccount().getUuid())); + && mFolder.getName().equals(other.getFolder().getName())); } @Override @@ -65,7 +64,6 @@ public abstract class Message implements Part, CompositeBody { int result = 1; result = MULTIPLIER * result + mFolder.getName().hashCode(); - result = MULTIPLIER * result + mFolder.getAccount().getUuid().hashCode(); result = MULTIPLIER * result + mUid.hashCode(); return result; } @@ -75,7 +73,7 @@ public abstract class Message implements Part, CompositeBody { } public void setUid(String uid) { - mReference = null; + this.mReference = null; this.mUid = uid; } @@ -249,15 +247,14 @@ public abstract class Message implements Part, CompositeBody { public void destroy() throws MessagingException {} @Override - public abstract void setEncoding(String encoding) throws UnavailableStorageException, MessagingException; + public abstract void setEncoding(String encoding) throws MessagingException; public abstract void setCharset(String charset) throws MessagingException; public MessageReference makeMessageReference() { if (mReference == null) { mReference = new MessageReference(); - mReference.accountUuid = getFolder().getAccount().getUuid(); - mReference.folderName = getFolder().getName(); + mReference.folderName = getFolder().getName(); mReference.uid = mUid; } return mReference; diff --git a/src/com/fsck/k9/mail/Store.java b/src/com/fsck/k9/mail/Store.java index 7dbfade7e..424b6bca1 100644 --- a/src/com/fsck/k9/mail/Store.java +++ b/src/com/fsck/k9/mail/Store.java @@ -1,24 +1,7 @@ package com.fsck.k9.mail; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import android.app.Application; -import android.content.Context; -import android.util.Log; - -import com.fsck.k9.Account; -import com.fsck.k9.K9; -import com.fsck.k9.mail.store.ImapStore; -import com.fsck.k9.mail.store.Pop3Store; -import com.fsck.k9.mail.store.StorageManager.StorageProvider; -import com.fsck.k9.mail.store.local.LocalStore; -import com.fsck.k9.mail.store.UnavailableStorageException; -import com.fsck.k9.mail.store.WebDavStore; /** * Store is the access point for an email message store. It's location can be @@ -29,191 +12,6 @@ import com.fsck.k9.mail.store.WebDavStore; * making as few network connections as possible. */ public abstract class Store { - protected static final int SOCKET_CONNECT_TIMEOUT = 30000; - protected static final int SOCKET_READ_TIMEOUT = 60000; - - /** - * Remote stores indexed by Uri. - */ - private static Map sStores = new HashMap(); - - /** - * Local stores indexed by UUID because the Uri may change due to migration to/from SD-card. - */ - private static ConcurrentMap sLocalStores = new ConcurrentHashMap(); - - /** - * Lock objects indexed by account UUID. - * - * @see #getLocalInstance(Account, Application) - */ - private static ConcurrentMap sAccountLocks = new ConcurrentHashMap(); - - /** - * Get an instance of a remote mail store. - */ - public synchronized static Store getRemoteInstance(Account account) throws MessagingException { - String uri = account.getStoreUri(); - - if (uri.startsWith("local")) { - throw new RuntimeException("Asked to get non-local Store object but given LocalStore URI"); - } - - Store store = sStores.get(uri); - if (store == null) { - if (uri.startsWith("imap")) { - store = new ImapStore(account); - } else if (uri.startsWith("pop3")) { - store = new Pop3Store(account); - } else if (uri.startsWith("webdav")) { - store = new WebDavStore(account); - } - - if (store != null) { - sStores.put(uri, store); - } - } - - if (store == null) { - throw new MessagingException("Unable to locate an applicable Store for " + uri); - } - - return store; - } - - /** - * Get an instance of a local mail store. - * - * @throws UnavailableStorageException - * if not {@link StorageProvider#isReady(Context)} - */ - public static LocalStore getLocalInstance(Account account, Application application) - throws MessagingException { - - String accountUuid = account.getUuid(); - - // Create new per-account lock object if necessary - sAccountLocks.putIfAbsent(accountUuid, new Object()); - - // Get the account's lock object - Object lock = sAccountLocks.get(accountUuid); - - // Use per-account locks so DatabaseUpgradeService always knows which account database is - // currently upgraded. - synchronized (lock) { - Store store = sLocalStores.get(accountUuid); - - if (store == null) { - // Creating a LocalStore instance will create or upgrade the database if - // necessary. This could take some time. - store = new LocalStore(account, application); - - sLocalStores.put(accountUuid, store); - } - - return (LocalStore) store; - } - } - - public static void removeAccount(Account account) { - try { - removeRemoteInstance(account); - } catch (Exception e) { - Log.e(K9.LOG_TAG, "Failed to reset remote store for account " + account.getUuid(), e); - } - - try { - removeLocalInstance(account); - } catch (Exception e) { - Log.e(K9.LOG_TAG, "Failed to reset local store for account " + account.getUuid(), e); - } - } - - /** - * Release reference to a local mail store instance. - * - * @param account - * {@link Account} instance that is used to get the local mail store instance. - */ - private static void removeLocalInstance(Account account) { - String accountUuid = account.getUuid(); - sLocalStores.remove(accountUuid); - } - - /** - * Release reference to a remote mail store instance. - * - * @param account - * {@link Account} instance that is used to get the remote mail store instance. - */ - private synchronized static void removeRemoteInstance(Account account) { - String uri = account.getStoreUri(); - - if (uri.startsWith("local")) { - throw new RuntimeException("Asked to get non-local Store object but given " + - "LocalStore URI"); - } - - sStores.remove(uri); - } - - /** - * Decodes the contents of store-specific URIs and puts them into a {@link ServerSettings} - * object. - * - * @param uri - * the store-specific URI to decode - * - * @return A {@link ServerSettings} object holding the settings contained in the URI. - * - * @see ImapStore#decodeUri(String) - * @see Pop3Store#decodeUri(String) - * @see WebDavStore#decodeUri(String) - */ - public static ServerSettings decodeStoreUri(String uri) { - if (uri.startsWith("imap")) { - return ImapStore.decodeUri(uri); - } else if (uri.startsWith("pop3")) { - return Pop3Store.decodeUri(uri); - } else if (uri.startsWith("webdav")) { - return WebDavStore.decodeUri(uri); - } else { - throw new IllegalArgumentException("Not a valid store URI"); - } - } - - /** - * Creates a store URI from the information supplied in the {@link ServerSettings} object. - * - * @param server - * The {@link ServerSettings} object that holds the server settings. - * - * @return A store URI that holds the same information as the {@code server} parameter. - * - * @see ImapStore#createUri(ServerSettings) - * @see Pop3Store#createUri(ServerSettings) - * @see WebDavStore#createUri(ServerSettings) - */ - public static String createStoreUri(ServerSettings server) { - if (ImapStore.STORE_TYPE.equals(server.type)) { - return ImapStore.createUri(server); - } else if (Pop3Store.STORE_TYPE.equals(server.type)) { - return Pop3Store.createUri(server); - } else if (WebDavStore.STORE_TYPE.equals(server.type)) { - return WebDavStore.createUri(server); - } else { - throw new IllegalArgumentException("Not a valid store URI"); - } - } - - - protected final Account mAccount; - - - protected Store(Account account) { - mAccount = account; - } - public abstract Folder getFolder(String name); public abstract List getPersonalNamespaces(boolean forceListAll) throws MessagingException; @@ -244,14 +42,9 @@ public abstract class Store { return true; } - public void sendMessages(List messages) throws MessagingException { - } + public void sendMessages(List messages) throws MessagingException { } public Pusher getPusher(PushReceiver receiver) { return null; } - - public Account getAccount() { - return mAccount; - } } diff --git a/src/com/fsck/k9/mail/store/ImapResponseParser.java b/src/com/fsck/k9/mail/store/ImapResponseParser.java index 4a8c8cc2e..541ff2376 100644 --- a/src/com/fsck/k9/mail/store/ImapResponseParser.java +++ b/src/com/fsck/k9/mail/store/ImapResponseParser.java @@ -11,7 +11,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.Locale; -public class ImapResponseParser { +class ImapResponseParser { private static final SimpleDateFormat mDateTimeFormat = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US); private static final SimpleDateFormat badDateTimeFormat = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z", Locale.US); private static final SimpleDateFormat badDateTimeFormat2 = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z", Locale.US); diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java index afd4384fd..98c3a6833 100644 --- a/src/com/fsck/k9/mail/store/ImapStore.java +++ b/src/com/fsck/k9/mail/store/ImapStore.java @@ -55,7 +55,6 @@ import android.os.PowerManager; import android.text.TextUtils; import android.util.Log; -import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.R; import com.fsck.k9.controller.MessageRetrievalListener; @@ -78,7 +77,6 @@ import com.fsck.k9.mail.Part; import com.fsck.k9.mail.PushReceiver; import com.fsck.k9.mail.Pusher; import com.fsck.k9.mail.ServerSettings; -import com.fsck.k9.mail.Store; import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.filter.EOLConvertingOutputStream; import com.fsck.k9.mail.filter.FixedLengthInputStream; @@ -105,7 +103,7 @@ import org.apache.commons.io.IOUtils; * TODO Need a default response handler for things like folder updates * */ -public class ImapStore extends Store { +public class ImapStore extends RemoteStore { public static final String STORE_TYPE = "IMAP"; private static final int IDLE_READ_TIMEOUT_INCREMENT = 5 * 60 * 1000; @@ -113,7 +111,7 @@ public class ImapStore extends Store { private static int MAX_DELAY_TIME = 5 * 60 * 1000; // 5 minutes private static int NORMAL_DELAY_TIME = 5000; - private static int FETCH_WINDOW_SIZE = 100; + private static final int FETCH_WINDOW_SIZE = 100; private Set mPermanentFlagsIndex = EnumSet.noneOf(Flag.class); @@ -387,7 +385,7 @@ public class ImapStore extends Store { @Override public boolean useCompression(final int type) { - return mAccount.useCompression(type); + return mStoreConfig.useCompression(type); } @Override @@ -439,12 +437,12 @@ public class ImapStore extends Store { */ private final Map mFolderCache = new HashMap(); - public ImapStore(Account account) throws MessagingException { - super(account); + public ImapStore(StoreConfig storeConfig) throws MessagingException { + super(storeConfig); ImapStoreSettings settings; try { - settings = decodeUri(mAccount.getStoreUri()); + settings = decodeUri(storeConfig.getStoreUri()); } catch (IllegalArgumentException e) { throw new MessagingException("Error while decoding store URI", e); } @@ -502,7 +500,7 @@ public class ImapStore extends Store { ImapConnection connection = getConnection(); try { List allFolders = listFolders(connection, false); - if (forceListAll || !mAccount.subscribedFoldersOnly()) { + if (forceListAll || !mStoreConfig.subscribedFoldersOnly()) { return allFolders; } else { List resultFolders = new LinkedList(); @@ -570,9 +568,9 @@ public class ImapStore extends Store { mCombinedPrefix = null; } - if (folder.equalsIgnoreCase(mAccount.getInboxFolderName())) { + if (folder.equalsIgnoreCase(mStoreConfig.getInboxFolderName())) { continue; - } else if (folder.equals(mAccount.getOutboxFolderName())) { + } else if (folder.equals(mStoreConfig.getOutboxFolderName())) { /* * There is a folder on the server with the same name as our local * outbox. Until we have a good plan to deal with this situation @@ -604,7 +602,7 @@ public class ImapStore extends Store { } } } - folders.add(getFolder(mAccount.getInboxFolderName())); + folders.add(getFolder(mStoreConfig.getInboxFolderName())); return folders; } @@ -661,17 +659,17 @@ public class ImapStore extends Store { for (int i = 0, count = attributes.size(); i < count; i++) { String attribute = attributes.getString(i); if (attribute.equals("\\Drafts")) { - mAccount.setDraftsFolderName(decodedFolderName); + mStoreConfig.setDraftsFolderName(decodedFolderName); if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected draft folder: " + decodedFolderName); } else if (attribute.equals("\\Sent")) { - mAccount.setSentFolderName(decodedFolderName); + mStoreConfig.setSentFolderName(decodedFolderName); if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected sent folder: " + decodedFolderName); } else if (attribute.equals("\\Spam") || attribute.equals("\\Junk")) { //rfc6154 just mentions \Junk - mAccount.setSpamFolderName(decodedFolderName); + mStoreConfig.setSpamFolderName(decodedFolderName); if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected spam folder: " + decodedFolderName); } else if (attribute.equals("\\Trash")) { - mAccount.setTrashFolderName(decodedFolderName); + mStoreConfig.setTrashFolderName(decodedFolderName); if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected trash folder: " + decodedFolderName); } } @@ -777,7 +775,7 @@ public class ImapStore extends Store { } - class ImapFolder extends Folder { + class ImapFolder extends Folder { private String mName; protected volatile int mMessageCount = -1; protected volatile long uidNext = -1L; @@ -789,14 +787,14 @@ public class ImapStore extends Store { private boolean mInSearch = false; public ImapFolder(ImapStore nStore, String name) { - super(nStore.getAccount()); + super(); store = nStore; this.mName = name; } public String getPrefixedName() throws MessagingException { String prefixedName = ""; - if (!mAccount.getInboxFolderName().equalsIgnoreCase(mName)) { + if (!mStoreConfig.getInboxFolderName().equalsIgnoreCase(mName)) { ImapConnection connection = null; synchronized (this) { if (mConnection == null) { @@ -1293,7 +1291,7 @@ public class ImapStore extends Store { } @Override - public Message getMessage(String uid) throws MessagingException { + public ImapMessage getMessage(String uid) throws MessagingException { return new ImapMessage(uid, this); } @@ -1351,7 +1349,7 @@ public class ImapStore extends Store { return search(searcher, listener); } - private List search(ImapSearcher searcher, MessageRetrievalListener listener) throws MessagingException { + private List search(ImapSearcher searcher, MessageRetrievalListener listener) throws MessagingException { checkOpen(); //only need READ access List messages = new ArrayList(); @@ -1432,7 +1430,7 @@ public class ImapStore extends Store { } @Override - public void fetch(List messages, FetchProfile fp, MessageRetrievalListener listener) + public void fetch(List messages, FetchProfile fp, MessageRetrievalListener listener) throws MessagingException { if (messages == null || messages.isEmpty()) { return; @@ -1468,8 +1466,8 @@ public class ImapStore extends Store { } if (fp.contains(FetchProfile.Item.BODY_SANE)) { // If the user wants to download unlimited-size messages, don't go only for the truncated body - if (mAccount.getMaximumAutoDownloadMessageSize() > 0) { - fetchFields.add(String.format(Locale.US, "BODY.PEEK[]<0.%d>", mAccount.getMaximumAutoDownloadMessageSize())); + if (mStoreConfig.getMaximumAutoDownloadMessageSize() > 0) { + fetchFields.add(String.format(Locale.US, "BODY.PEEK[]<0.%d>", mStoreConfig.getMaximumAutoDownloadMessageSize())); } else { fetchFields.add("BODY.PEEK[]"); } @@ -1544,7 +1542,7 @@ public class ImapStore extends Store { } if (listener != null) { - listener.messageFinished(message, messageNumber, messageMap.size()); + listener.messageFinished(imapMessage, messageNumber, messageMap.size()); } } else { handleUntaggedResponse(response); @@ -1572,7 +1570,7 @@ public class ImapStore extends Store { String partId = parts[0]; if ("TEXT".equalsIgnoreCase(partId)) { fetch = String.format(Locale.US, "BODY.PEEK[TEXT]<0.%d>", - mAccount.getMaximumAutoDownloadMessageSize()); + mStoreConfig.getMaximumAutoDownloadMessageSize()); } else { fetch = String.format("BODY.PEEK[%s]", partId); } @@ -2194,7 +2192,7 @@ public class ImapStore extends Store { } protected String getLogId() { - String id = getAccount().getDescription() + ":" + getName() + "/" + Thread.currentThread().getName(); + String id = mStoreConfig.toString() + ":" + getName() + "/" + Thread.currentThread().getName(); if (mConnection != null) { id += "/" + mConnection.getLogId(); } @@ -2213,7 +2211,7 @@ public class ImapStore extends Store { public List search(final String queryString, final Set requiredFlags, final Set forbiddenFlags) throws MessagingException { - if (!mAccount.allowRemoteSearch()) { + if (!mStoreConfig.allowRemoteSearch()) { throw new MessagingException("Your settings do not allow remote searching of this account"); } @@ -2287,7 +2285,7 @@ public class ImapStore extends Store { } } final String encodedQry = encodeString(queryString); - if (mAccount.isRemoteSearchFullText()) { + if (mStoreConfig.isRemoteSearchFullText()) { imapQuery += "TEXT " + encodedQry; } else { imapQuery += "OR SUBJECT " + encodedQry + " FROM " + encodedQry; @@ -2424,7 +2422,7 @@ public class ImapStore extends Store { } } - setReadTimeout(Store.SOCKET_READ_TIMEOUT); + setReadTimeout(SOCKET_READ_TIMEOUT); mIn = new PeekableInputStream(new BufferedInputStream(mSocket.getInputStream(), 1024)); @@ -2458,7 +2456,7 @@ public class ImapStore extends Store { mSocket = TrustedSocketFactory.createSocket(mSocket, mSettings.getHost(), mSettings.getPort(), mSettings.getClientCertificateAlias()); - mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT); + mSocket.setSoTimeout(SOCKET_READ_TIMEOUT); mIn = new PeekableInputStream(new BufferedInputStream(mSocket .getInputStream(), 1024)); mParser = new ImapResponseParser(mIn); @@ -2953,7 +2951,7 @@ public class ImapStore extends Store { super(store, name); receiver = nReceiver; TracingPowerManager pm = TracingPowerManager.getPowerManager(receiver.getContext()); - wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ImapFolderPusher " + store.getAccount().getDescription() + ":" + getName()); + wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ImapFolderPusher " + mStoreConfig.toString() + ":" + getName()); wakeLock.setReferenceCounted(false); } @@ -2968,7 +2966,7 @@ public class ImapStore extends Store { if (doneSent.compareAndSet(false, true)) { ImapConnection conn = mConnection; if (conn != null) { - conn.setReadTimeout(Store.SOCKET_READ_TIMEOUT); + conn.setReadTimeout(SOCKET_READ_TIMEOUT); sendContinuation("DONE"); } @@ -3030,7 +3028,7 @@ public class ImapStore extends Store { throw new MessagingException("IMAP server is not IDLE capable:" + conn.toString()); } - if (!stop.get() && mAccount.isPushPollOnConnect() && (conn != oldConnection || needsPoll.getAndSet(false))) { + if (!stop.get() && mStoreConfig.isPushPollOnConnect() && (conn != oldConnection || needsPoll.getAndSet(false))) { List untaggedResponses = new ArrayList(storedUntaggedResponses); storedUntaggedResponses.clear(); processUntaggedResponses(untaggedResponses); @@ -3061,8 +3059,8 @@ public class ImapStore extends Store { } } - if (startUid < newUidNext - mAccount.getDisplayCount()) { - startUid = newUidNext - mAccount.getDisplayCount(); + if (startUid < newUidNext - mStoreConfig.getDisplayCount()) { + startUid = newUidNext - mStoreConfig.getDisplayCount(); } if (startUid < 1) { startUid = 1; @@ -3099,7 +3097,7 @@ public class ImapStore extends Store { idling.set(true); doneSent.set(false); - conn.setReadTimeout((getAccount().getIdleRefreshMinutes() * 60 * 1000) + IDLE_READ_TIMEOUT_INCREMENT); + conn.setReadTimeout((mStoreConfig.getIdleRefreshMinutes() * 60 * 1000) + IDLE_READ_TIMEOUT_INCREMENT); untaggedResponses = executeSimpleCommand(COMMAND_IDLE, false, ImapFolderPusher.this); idling.set(false); delayTime.set(NORMAL_DELAY_TIME); @@ -3495,7 +3493,7 @@ public class ImapStore extends Store { @Override public int getRefreshInterval() { - return (getAccount().getIdleRefreshMinutes() * 60 * 1000); + return (mStoreConfig.getIdleRefreshMinutes() * 60 * 1000); } @Override diff --git a/src/com/fsck/k9/mail/store/Pop3Store.java b/src/com/fsck/k9/mail/store/Pop3Store.java index 5e2ee632a..593191a14 100644 --- a/src/com/fsck/k9/mail/store/Pop3Store.java +++ b/src/com/fsck/k9/mail/store/Pop3Store.java @@ -3,7 +3,6 @@ package com.fsck.k9.mail.store; import android.util.Log; -import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.R; import com.fsck.k9.controller.MessageRetrievalListener; @@ -24,7 +23,6 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.LinkedList; @@ -35,7 +33,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; -public class Pop3Store extends Store { +public class Pop3Store extends RemoteStore { public static final String STORE_TYPE = "POP3"; private static final String STLS_COMMAND = "STLS"; @@ -209,12 +207,12 @@ public class Pop3Store extends Store { private boolean mTopNotSupported; - public Pop3Store(Account account) throws MessagingException { - super(account); + public Pop3Store(StoreConfig storeConfig) throws MessagingException { + super(storeConfig); ServerSettings settings; try { - settings = decodeUri(mAccount.getStoreUri()); + settings = decodeUri(storeConfig.getStoreUri()); } catch (IllegalArgumentException e) { throw new MessagingException("Error while decoding store URI", e); } @@ -243,13 +241,13 @@ public class Pop3Store extends Store { @Override public List getPersonalNamespaces(boolean forceListAll) throws MessagingException { List folders = new LinkedList(); - folders.add(getFolder(mAccount.getInboxFolderName())); + folders.add(getFolder(mStoreConfig.getInboxFolderName())); return folders; } @Override public void checkSettings() throws MessagingException { - Pop3Folder folder = new Pop3Folder(mAccount.getInboxFolderName()); + Pop3Folder folder = new Pop3Folder(mStoreConfig.getInboxFolderName()); folder.open(Folder.OPEN_MODE_RW); if (!mCapabilities.uidl) { /* @@ -272,7 +270,7 @@ public class Pop3Store extends Store { return false; } - class Pop3Folder extends Folder { + class Pop3Folder extends Folder { private Socket mSocket; private InputStream mIn; private OutputStream mOut; @@ -283,11 +281,11 @@ public class Pop3Store extends Store { private int mMessageCount; public Pop3Folder(String name) { - super(Pop3Store.this.mAccount); + super(); this.mName = name; - if (mName.equalsIgnoreCase(mAccount.getInboxFolderName())) { - mName = mAccount.getInboxFolderName(); + if (mName.equalsIgnoreCase(mStoreConfig.getInboxFolderName())) { + mName = mStoreConfig.getInboxFolderName(); } } @@ -297,7 +295,7 @@ public class Pop3Store extends Store { return; } - if (!mName.equalsIgnoreCase(mAccount.getInboxFolderName())) { + if (!mName.equalsIgnoreCase(mStoreConfig.getInboxFolderName())) { throw new MessagingException("Folder does not exist"); } @@ -313,7 +311,7 @@ public class Pop3Store extends Store { mIn = new BufferedInputStream(mSocket.getInputStream(), 1024); mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512); - mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT); + mSocket.setSoTimeout(SOCKET_READ_TIMEOUT); if (!isOpen()) { throw new MessagingException("Unable to connect socket"); } @@ -328,7 +326,7 @@ public class Pop3Store extends Store { mSocket = TrustedSocketFactory.createSocket(mSocket, mHost, mPort, mClientCertificateAlias); - mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT); + mSocket.setSoTimeout(SOCKET_READ_TIMEOUT); mIn = new BufferedInputStream(mSocket.getInputStream(), 1024); mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512); if (!isOpen()) { @@ -541,7 +539,7 @@ public class Pop3Store extends Store { @Override public boolean exists() throws MessagingException { - return mName.equalsIgnoreCase(mAccount.getInboxFolderName()); + return mName.equalsIgnoreCase(mStoreConfig.getInboxFolderName()); } @Override @@ -559,7 +557,7 @@ public class Pop3Store extends Store { } @Override - public Message getMessage(String uid) throws MessagingException { + public Pop3Message getMessage(String uid) throws MessagingException { Pop3Message message = mUidToMsgMap.get(uid); if (message == null) { message = new Pop3Message(uid, this); @@ -761,7 +759,7 @@ public class Pop3Store extends Store { * @throws MessagingException */ @Override - public void fetch(List messages, FetchProfile fp, MessageRetrievalListener listener) + public void fetch(List messages, FetchProfile fp, MessageRetrievalListener listener) throws MessagingException { if (messages == null || messages.isEmpty()) { return; @@ -805,9 +803,9 @@ public class Pop3Store extends Store { * To convert the suggested download size we take the size * divided by the maximum line size (76). */ - if (mAccount.getMaximumAutoDownloadMessageSize() > 0) { + if (mStoreConfig.getMaximumAutoDownloadMessageSize() > 0) { fetchBody(pop3Message, - (mAccount.getMaximumAutoDownloadMessageSize() / 76)); + (mStoreConfig.getMaximumAutoDownloadMessageSize() / 76)); } else { fetchBody(pop3Message, -1); } @@ -819,7 +817,7 @@ public class Pop3Store extends Store { pop3Message.setBody(null); } if (listener != null && !(fp.contains(FetchProfile.Item.ENVELOPE) && fp.size() == 1)) { - listener.messageFinished(message, i, count); + listener.messageFinished(pop3Message, i, count); } } catch (IOException ioe) { throw new MessagingException("Unable to fetch message", ioe); diff --git a/src/com/fsck/k9/mail/store/RemoteStore.java b/src/com/fsck/k9/mail/store/RemoteStore.java new file mode 100644 index 000000000..9cad2aac2 --- /dev/null +++ b/src/com/fsck/k9/mail/store/RemoteStore.java @@ -0,0 +1,122 @@ +package com.fsck.k9.mail.store; + +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.ServerSettings; +import com.fsck.k9.mail.Store; + +import java.util.HashMap; +import java.util.Map; + +public abstract class RemoteStore extends Store { + protected static final int SOCKET_CONNECT_TIMEOUT = 30000; + protected static final int SOCKET_READ_TIMEOUT = 60000; + + protected StoreConfig mStoreConfig; + + /** + * Remote stores indexed by Uri. + */ + private static Map sStores = new HashMap(); + + + public RemoteStore(StoreConfig storeConfig) { + mStoreConfig = storeConfig; + } + + /** + * Get an instance of a remote mail store. + */ + public synchronized static Store getInstance(StoreConfig storeConfig) throws MessagingException { + String uri = storeConfig.getStoreUri(); + + if (uri.startsWith("local")) { + throw new RuntimeException("Asked to get non-local Store object but given LocalStore URI"); + } + + Store store = sStores.get(uri); + if (store == null) { + if (uri.startsWith("imap")) { + store = new ImapStore(storeConfig); + } else if (uri.startsWith("pop3")) { + store = new Pop3Store(storeConfig); + } else if (uri.startsWith("webdav")) { + store = new WebDavStore(storeConfig); + } + + if (store != null) { + sStores.put(uri, store); + } + } + + if (store == null) { + throw new MessagingException("Unable to locate an applicable Store for " + uri); + } + + return store; + } + + /** + * Release reference to a remote mail store instance. + * + * @param storeConfig {@link com.fsck.k9.mail.store.StoreConfig} instance that is used to get the remote mail store instance. + */ + public static void removeInstance(StoreConfig storeConfig) { + String uri = storeConfig.getStoreUri(); + + if (uri.startsWith("local")) { + throw new RuntimeException("Asked to get non-local Store object but given " + + "LocalStore URI"); + } + + sStores.remove(uri); + } + + /** + * Decodes the contents of store-specific URIs and puts them into a {@link com.fsck.k9.mail.ServerSettings} + * object. + * + * @param uri + * the store-specific URI to decode + * + * @return A {@link com.fsck.k9.mail.ServerSettings} object holding the settings contained in the URI. + * + * @see com.fsck.k9.mail.store.ImapStore#decodeUri(String) + * @see com.fsck.k9.mail.store.Pop3Store#decodeUri(String) + * @see com.fsck.k9.mail.store.WebDavStore#decodeUri(String) + */ + public static ServerSettings decodeStoreUri(String uri) { + if (uri.startsWith("imap")) { + return ImapStore.decodeUri(uri); + } else if (uri.startsWith("pop3")) { + return Pop3Store.decodeUri(uri); + } else if (uri.startsWith("webdav")) { + return WebDavStore.decodeUri(uri); + } else { + throw new IllegalArgumentException("Not a valid store URI"); + } + } + + /** + * Creates a store URI from the information supplied in the {@link com.fsck.k9.mail.ServerSettings} object. + * + * @param server + * The {@link com.fsck.k9.mail.ServerSettings} object that holds the server settings. + * + * @return A store URI that holds the same information as the {@code server} parameter. + * + * @see com.fsck.k9.mail.store.ImapStore#createUri(com.fsck.k9.mail.ServerSettings) + * @see com.fsck.k9.mail.store.Pop3Store#createUri(com.fsck.k9.mail.ServerSettings) + * @see com.fsck.k9.mail.store.WebDavStore#createUri(com.fsck.k9.mail.ServerSettings) + */ + public static String createStoreUri(ServerSettings server) { + if (ImapStore.STORE_TYPE.equals(server.type)) { + return ImapStore.createUri(server); + } else if (Pop3Store.STORE_TYPE.equals(server.type)) { + return Pop3Store.createUri(server); + } else if (WebDavStore.STORE_TYPE.equals(server.type)) { + return WebDavStore.createUri(server); + } else { + throw new IllegalArgumentException("Not a valid store URI"); + } + } +} diff --git a/src/com/fsck/k9/mail/store/StoreConfig.java b/src/com/fsck/k9/mail/store/StoreConfig.java new file mode 100644 index 000000000..ba83ba201 --- /dev/null +++ b/src/com/fsck/k9/mail/store/StoreConfig.java @@ -0,0 +1,32 @@ +package com.fsck.k9.mail.store; + +public interface StoreConfig { + String getUuid(); + String getStoreUri(); + String getTransportUri(); + + boolean subscribedFoldersOnly(); + boolean useCompression(int type); + + String getInboxFolderName(); + String getOutboxFolderName(); + String getDraftsFolderName(); + + void setInboxFolderName(String folderName); + void setDraftsFolderName(String decodedFolderName); + void setTrashFolderName(String decodedFolderName); + void setSpamFolderName(String decodedFolderName); + void setSentFolderName(String decodedFolderName); + void setAutoExpandFolderName(String folderName); + + int getMaximumAutoDownloadMessageSize(); + + boolean allowRemoteSearch(); + boolean isRemoteSearchFullText(); + + boolean isPushPollOnConnect(); + + int getDisplayCount(); + + int getIdleRefreshMinutes(); +} diff --git a/src/com/fsck/k9/mail/store/WebDavStore.java b/src/com/fsck/k9/mail/store/WebDavStore.java index 27d0c7ccb..61dba1088 100644 --- a/src/com/fsck/k9/mail/store/WebDavStore.java +++ b/src/com/fsck/k9/mail/store/WebDavStore.java @@ -2,7 +2,6 @@ package com.fsck.k9.mail.store; import android.util.Log; -import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.controller.MessageRetrievalListener; @@ -55,7 +54,7 @@ import java.util.zip.GZIPInputStream; * and email information. * */ -public class WebDavStore extends Store { +public class WebDavStore extends RemoteStore { public static final String STORE_TYPE = "WebDAV"; // Authentication types @@ -294,12 +293,12 @@ public class WebDavStore extends Store { private Map mFolderList = new HashMap(); - public WebDavStore(Account account) throws MessagingException { - super(account); + public WebDavStore(StoreConfig storeConfig) throws MessagingException { + super(storeConfig); WebDavStoreSettings settings; try { - settings = decodeUri(mAccount.getStoreUri()); + settings = decodeUri(storeConfig.getStoreUri()); } catch (IllegalArgumentException e) { throw new MessagingException("Error while decoding store URI", e); } @@ -380,21 +379,21 @@ public class WebDavStore extends Store { Map specialFoldersMap = dataset.getSpecialFolderToUrl(); String folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_INBOX_FOLDER)); if (folderName != null) { - mAccount.setAutoExpandFolderName(folderName); - mAccount.setInboxFolderName(folderName); + mStoreConfig.setAutoExpandFolderName(folderName); + mStoreConfig.setInboxFolderName(folderName); } folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_DRAFTS_FOLDER)); if (folderName != null) - mAccount.setDraftsFolderName(folderName); + mStoreConfig.setDraftsFolderName(folderName); folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_TRASH_FOLDER)); if (folderName != null) - mAccount.setTrashFolderName(folderName); + mStoreConfig.setTrashFolderName(folderName); folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SPAM_FOLDER)); if (folderName != null) - mAccount.setSpamFolderName(folderName); + mStoreConfig.setSpamFolderName(folderName); // K-9 Mail's outbox is a special local folder and different from Exchange/WebDAV's outbox. /* @@ -405,7 +404,7 @@ public class WebDavStore extends Store { folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SENT_FOLDER)); if (folderName != null) - mAccount.setSentFolderName(folderName); + mStoreConfig.setSentFolderName(folderName); /** * Next we get all the folders (including "special" ones) @@ -1196,7 +1195,7 @@ public class WebDavStore extends Store { @Override public void sendMessages(List messages) throws MessagingException { - WebDavFolder tmpFolder = (WebDavStore.WebDavFolder) getFolder(mAccount.getDraftsFolderName()); + WebDavFolder tmpFolder = (WebDavStore.WebDavFolder) getFolder(mStoreConfig.getDraftsFolderName()); try { tmpFolder.open(Folder.OPEN_MODE_RW); List retMessages = tmpFolder.appendWebDavMessages(messages); @@ -1216,7 +1215,7 @@ public class WebDavStore extends Store { /** * A WebDav Folder */ - class WebDavFolder extends Folder { + class WebDavFolder extends Folder { private String mName; private String mFolderUrl; private boolean mIsOpen = false; @@ -1229,7 +1228,7 @@ public class WebDavStore extends Store { } public WebDavFolder(WebDavStore nStore, String name) { - super(nStore.getAccount()); + super(); store = nStore; this.mName = name; @@ -1397,7 +1396,7 @@ public class WebDavStore extends Store { } @Override - public Message getMessage(String uid) throws MessagingException { + public WebDavMessage getMessage(String uid) throws MessagingException { return new WebDavMessage(uid, this); } @@ -1498,7 +1497,7 @@ public class WebDavStore extends Store { } @Override - public void fetch(List messages, FetchProfile fp, MessageRetrievalListener listener) + public void fetch(List messages, FetchProfile fp, MessageRetrievalListener listener) throws MessagingException { if (messages == null || messages.isEmpty()) { @@ -1519,8 +1518,8 @@ public class WebDavStore extends Store { } if (fp.contains(FetchProfile.Item.BODY_SANE)) { - if (mAccount.getMaximumAutoDownloadMessageSize() > 0) { - fetchMessages(messages, listener, (mAccount.getMaximumAutoDownloadMessageSize() / 76)); + if (mStoreConfig.getMaximumAutoDownloadMessageSize() > 0) { + fetchMessages(messages, listener, (mStoreConfig.getMaximumAutoDownloadMessageSize() / 76)); } else { fetchMessages(messages, listener, -1); } @@ -1533,7 +1532,7 @@ public class WebDavStore extends Store { /** * Fetches the full messages or up to lines lines and passes them to the message parser. */ - private void fetchMessages(List messages, MessageRetrievalListener listener, int lines) + private void fetchMessages(List messages, MessageRetrievalListener listener, int lines) throws MessagingException { WebDavHttpClient httpclient; httpclient = getHttpClient(); @@ -1594,8 +1593,8 @@ public class WebDavStore extends Store { if (entity != null) { InputStream istream = null; StringBuilder buffer = new StringBuilder(); - String tempText = ""; - String resultText = ""; + String tempText; + String resultText; BufferedReader reader = null; int currentLines = 0; diff --git a/src/com/fsck/k9/mail/store/local/LocalFolder.java b/src/com/fsck/k9/mail/store/local/LocalFolder.java index 56e2558e4..22c3674ee 100644 --- a/src/com/fsck/k9/mail/store/local/LocalFolder.java +++ b/src/com/fsck/k9/mail/store/local/LocalFolder.java @@ -28,6 +28,7 @@ import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.util.Log; +import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.Account.MessageFormat; import com.fsck.k9.activity.Search; @@ -58,7 +59,7 @@ import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback; import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException; import com.fsck.k9.provider.AttachmentProvider; -public class LocalFolder extends Folder implements Serializable { +public class LocalFolder extends Folder implements Serializable { private static final long serialVersionUID = -1973296520918624767L; @@ -80,22 +81,19 @@ public class LocalFolder extends Folder implements Serializable { private Integer mLastUid = null; public LocalFolder(LocalStore localStore, String name) { - super(localStore.getAccount()); + super(); this.localStore = localStore; this.mName = name; - if (this.localStore.getAccount().getInboxFolderName().equals(getName())) { - + if (getAccount().getInboxFolderName().equals(getName())) { mSyncClass = FolderClass.FIRST_CLASS; mPushClass = FolderClass.FIRST_CLASS; mInTopGroup = true; } - - } public LocalFolder(LocalStore localStore, long id) { - super(localStore.getAccount()); + super(); this.localStore = localStore; this.mFolderId = id; } @@ -104,6 +102,25 @@ public class LocalFolder extends Folder implements Serializable { return mFolderId; } + public String getUuid() + { + return getAccount().getUuid(); + } + + public boolean getSignatureUse() { + return getAccount().getSignatureUse(); + } + + public void setLastSelectedFolderName(String destFolderName) { + getAccount().setLastSelectedFolderName(destFolderName); + } + + public boolean syncRemoteDeletions() { + return getAccount().syncRemoteDeletions(); + } + + + @Override public void open(final int mode) throws MessagingException { @@ -216,7 +233,7 @@ public class LocalFolder extends Folder implements Serializable { @Override public boolean create(FolderType type) throws MessagingException { - return create(type, mAccount.getDisplayCount()); + return create(type, getAccount().getDisplayCount()); } @Override @@ -517,25 +534,25 @@ public class LocalFolder extends Folder implements Serializable { String id = getPrefId(); // there can be a lot of folders. For the defaults, let's not save prefs, saving space, except for INBOX - if (mDisplayClass == FolderClass.NO_CLASS && !mAccount.getInboxFolderName().equals(getName())) { + if (mDisplayClass == FolderClass.NO_CLASS && !getAccount().getInboxFolderName().equals(getName())) { editor.remove(id + ".displayMode"); } else { editor.putString(id + ".displayMode", mDisplayClass.name()); } - if (mSyncClass == FolderClass.INHERITED && !mAccount.getInboxFolderName().equals(getName())) { + if (mSyncClass == FolderClass.INHERITED && !getAccount().getInboxFolderName().equals(getName())) { editor.remove(id + ".syncMode"); } else { editor.putString(id + ".syncMode", mSyncClass.name()); } - if (mNotifyClass == FolderClass.INHERITED && !mAccount.getInboxFolderName().equals(getName())) { + if (mNotifyClass == FolderClass.INHERITED && !getAccount().getInboxFolderName().equals(getName())) { editor.remove(id + ".notifyMode"); } else { editor.putString(id + ".notifyMode", mNotifyClass.name()); } - if (mPushClass == FolderClass.SECOND_CLASS && !mAccount.getInboxFolderName().equals(getName())) { + if (mPushClass == FolderClass.SECOND_CLASS && !getAccount().getInboxFolderName().equals(getName())) { editor.remove(id + ".pushMode"); } else { editor.putString(id + ".pushMode", mPushClass.name()); @@ -597,7 +614,7 @@ public class LocalFolder extends Folder implements Serializable { } @Override - public void fetch(final List messages, final FetchProfile fp, final MessageRetrievalListener listener) + public void fetch(final List messages, final FetchProfile fp, final MessageRetrievalListener listener) throws MessagingException { try { this.localStore.database.execute(false, new DbCallback() { @@ -614,7 +631,7 @@ public class LocalFolder extends Folder implements Serializable { try { cursor = db.rawQuery("SELECT html_content, text_content, mime_type FROM messages " + "WHERE id = ?", - new String[] { Long.toString(localMessage.mId) }); + new String[] { Long.toString(localMessage.getId()) }); cursor.moveToNext(); String htmlContent = cursor.getString(0); String textContent = cursor.getString(1); @@ -629,7 +646,7 @@ public class LocalFolder extends Folder implements Serializable { mp.addBodyPart(bp); } - if (mAccount.getMessageFormat() != MessageFormat.TEXT) { + if (getAccount().getMessageFormat() != MessageFormat.TEXT) { if (htmlContent != null) { TextBody body = new TextBody(htmlContent); MimeBodyPart bp = new MimeBodyPart(body, "text/html"); @@ -700,7 +717,7 @@ public class LocalFolder extends Folder implements Serializable { "content_disposition" }, "message_id = ?", - new String[] { Long.toString(localMessage.mId) }, + new String[] { Long.toString(localMessage.getId()) }, null, null, null); @@ -1439,12 +1456,12 @@ public class LocalFolder extends Folder implements Serializable { message.isSet(Flag.FLAGGED) ? 1 : 0, message.isSet(Flag.ANSWERED) ? 1 : 0, message.isSet(Flag.FORWARDED) ? 1 : 0, - message.mId + message.getId() }); for (int i = 0, count = attachments.size(); i < count; i++) { Part attachment = attachments.get(i); - saveAttachment(message.mId, attachment, false); + saveAttachment(message.getId(), attachment, false); } saveHeaders(message.getId(), message); } catch (Exception e) { @@ -1637,7 +1654,7 @@ public class LocalFolder extends Folder implements Serializable { File attachmentFile = new File(attachmentDirectory, Long.toString(attachmentId)); tempAttachmentFile.renameTo(attachmentFile); contentUri = AttachmentProvider.getAttachmentUri( - mAccount, + getAccount(), attachmentId); if (MimeUtil.isMessage(attachment.getMimeType())) { attachment.setBody(new LocalAttachmentMessageBody( @@ -1712,7 +1729,7 @@ public class LocalFolder extends Folder implements Serializable { @Override public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { db.update("messages", cv, "id = ?", new String[] - { Long.toString(message.mId) }); + { Long.toString(message.getId()) }); return null; } }); @@ -1829,7 +1846,7 @@ public class LocalFolder extends Folder implements Serializable { setPushState(null); setLastPush(0); setLastChecked(0); - setVisibleLimit(mAccount.getDisplayCount()); + setVisibleLimit(getAccount().getDisplayCount()); } @Override @@ -1878,7 +1895,7 @@ public class LocalFolder extends Folder implements Serializable { public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { Cursor attachmentsCursor = null; try { - String accountUuid = mAccount.getUuid(); + String accountUuid = getUuid(); Context context = LocalFolder.this.localStore.mApplication; // Get attachment IDs @@ -2039,7 +2056,7 @@ public class LocalFolder extends Folder implements Serializable { // Append the first message ID from the "In-Reply-To" header line String[] inReplyToArray = message.getHeader("In-Reply-To"); - String inReplyTo = null; + String inReplyTo; if (inReplyToArray != null && inReplyToArray.length > 0) { inReplyTo = Utility.extractMessageId(inReplyToArray[0]); if (inReplyTo != null) { @@ -2194,4 +2211,8 @@ public class LocalFolder extends Folder implements Serializable { throw(MessagingException) e.getCause(); } } + + private Account getAccount() { + return localStore.getAccount(); + } } diff --git a/src/com/fsck/k9/mail/store/local/LocalMessage.java b/src/com/fsck/k9/mail/store/local/LocalMessage.java index 56e20ea8d..6dd164f14 100644 --- a/src/com/fsck/k9/mail/store/local/LocalMessage.java +++ b/src/com/fsck/k9/mail/store/local/LocalMessage.java @@ -12,7 +12,9 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.util.Log; +import com.fsck.k9.Account; import com.fsck.k9.K9; +import com.fsck.k9.activity.MessageReference; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; @@ -28,7 +30,7 @@ public class LocalMessage extends MimeMessage { private final LocalStore localStore; - long mId; + private long mId; private int mAttachmentCount; private String mSubject; @@ -557,4 +559,43 @@ public class LocalMessage extends MimeMessage { public long getRootId() { return mRootId; } + + public Account getAccount() { + return localStore.getAccount(); + } + + @Override + public MessageReference makeMessageReference() { + if (mReference == null) { + mReference = super.makeMessageReference(); + mReference.accountUuid = getFolder().getUuid(); + } + return mReference; + } + + @Override + public LocalFolder getFolder() { + return (LocalFolder) super.getFolder(); + } + + public String getUri() { + return "email://messages/" + getAccount().getAccountNumber() + "/" + getFolder().getName() + "/" + getUid(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + + LocalMessage that = (LocalMessage) o; + return !(getUid() != null ? !getUid().equals(that.getUid()) : that.getUid() != null); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (getUid() != null ? getUid().hashCode() : 0); + return result; + } } \ No newline at end of file diff --git a/src/com/fsck/k9/mail/store/local/LocalStore.java b/src/com/fsck/k9/mail/store/local/LocalStore.java index 02002c125..f4c82743a 100644 --- a/src/com/fsck/k9/mail/store/local/LocalStore.java +++ b/src/com/fsck/k9/mail/store/local/LocalStore.java @@ -10,6 +10,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import android.app.Application; import android.content.ContentResolver; @@ -32,6 +34,8 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Store; +import com.fsck.k9.mail.store.RemoteStore; +import com.fsck.k9.mail.store.StoreConfig; import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback; import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException; import com.fsck.k9.mail.store.StorageManager; @@ -56,6 +60,18 @@ public class LocalStore extends Store implements Serializable { static final String[] EMPTY_STRING_ARRAY = new String[0]; static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + /** + * Lock objects indexed by account UUID. + * + * @see #getInstance(Account, Application) + */ + private static ConcurrentMap sAccountLocks = new ConcurrentHashMap(); + + /** + * Local stores indexed by UUID because the Uri may change due to migration to/from SD-card. + */ + private static ConcurrentMap sLocalStores = new ConcurrentHashMap(); + /* * a String containing the columns getMessages expects to work with * in the correct order. @@ -138,6 +154,7 @@ public class LocalStore extends Store implements Serializable { LockableDatabase database; private ContentResolver mContentResolver; + private final Account mAccount; /** * local://localhost/path/to/database/uuid.db @@ -147,7 +164,7 @@ public class LocalStore extends Store implements Serializable { * @throws UnavailableStorageException if not {@link StorageProvider#isReady(Context)} */ public LocalStore(final Account account, final Application application) throws MessagingException { - super(account); + mAccount = account; database = new LockableDatabase(application, account.getUuid(), new StoreSchemaDefinition(this)); mApplication = application; @@ -158,10 +175,73 @@ public class LocalStore extends Store implements Serializable { database.open(); } + /** + * Get an instance of a local mail store. + * + * @throws UnavailableStorageException + * if not {@link StorageProvider#isReady(Context)} + */ + public static LocalStore getInstance(Account account, Application application) + throws MessagingException { + + String accountUuid = account.getUuid(); + + // Create new per-account lock object if necessary + sAccountLocks.putIfAbsent(accountUuid, new Object()); + + // Get the account's lock object + Object lock = sAccountLocks.get(accountUuid); + + // Use per-account locks so DatabaseUpgradeService always knows which account database is + // currently upgraded. + synchronized (lock) { + Store store = sLocalStores.get(accountUuid); + + if (store == null) { + // Creating a LocalStore instance will create or upgrade the database if + // necessary. This could take some time. + store = new LocalStore(account, application); + + sLocalStores.put(accountUuid, store); + } + + return (LocalStore) store; + } + } + + public static void removeAccount(StoreConfig storeConfig) { + try { + RemoteStore.removeInstance(storeConfig); + } catch (Exception e) { + Log.e(K9.LOG_TAG, "Failed to reset remote store for account " + storeConfig.getUuid(), e); + } + + try { + removeInstance(storeConfig); + } catch (Exception e) { + Log.e(K9.LOG_TAG, "Failed to reset local store for account " + storeConfig.getUuid(), e); + } + } + + /** + * Release reference to a local mail store instance. + * + * @param account + * {@link Account} instance that is used to get the local mail store instance. + */ + private static void removeInstance(StoreConfig account) { + String accountUuid = account.getUuid(); + sLocalStores.remove(accountUuid); + } + public void switchLocalStorage(final String newStorageProviderId) throws MessagingException { database.switchProvider(newStorageProviderId); } + protected Account getAccount() { + return mAccount; + } + protected SharedPreferences getPreferences() { return Preferences.getPreferences(mApplication).getPreferences(); } diff --git a/src/com/fsck/k9/mail/transport/SmtpTransport.java b/src/com/fsck/k9/mail/transport/SmtpTransport.java index 428c0dce2..a5365a33a 100644 --- a/src/com/fsck/k9/mail/transport/SmtpTransport.java +++ b/src/com/fsck/k9/mail/transport/SmtpTransport.java @@ -15,6 +15,7 @@ import com.fsck.k9.mail.filter.LineWrapOutputStream; import com.fsck.k9.mail.filter.PeekableInputStream; import com.fsck.k9.mail.filter.SmtpDataStuffing; import com.fsck.k9.mail.internet.MimeUtility; +import com.fsck.k9.mail.store.StoreConfig; import com.fsck.k9.mail.store.local.LocalMessage; import com.fsck.k9.net.ssl.TrustedSocketFactory; @@ -183,7 +184,7 @@ public class SmtpTransport extends Transport { private boolean m8bitEncodingAllowed; private int mLargestAcceptableMessage; - public SmtpTransport(Account account) throws MessagingException { + public SmtpTransport(StoreConfig account) throws MessagingException { ServerSettings settings; try { settings = decodeUri(account.getTransportUri()); diff --git a/src/com/fsck/k9/mail/transport/WebDavTransport.java b/src/com/fsck/k9/mail/transport/WebDavTransport.java index 1c94d01d5..40b8b6884 100644 --- a/src/com/fsck/k9/mail/transport/WebDavTransport.java +++ b/src/com/fsck/k9/mail/transport/WebDavTransport.java @@ -3,12 +3,12 @@ package com.fsck.k9.mail.transport; import android.util.Log; -import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.Transport; +import com.fsck.k9.mail.store.StoreConfig; import com.fsck.k9.mail.store.WebDavStore; import java.util.Collections; @@ -43,12 +43,8 @@ public class WebDavTransport extends Transport { private WebDavStore store; - public WebDavTransport(Account account) throws MessagingException { - if (account.getRemoteStore() instanceof WebDavStore) { - store = (WebDavStore) account.getRemoteStore(); - } else { - store = new WebDavStore(account); - } + public WebDavTransport(StoreConfig configInterface) throws MessagingException { + store = new WebDavStore(configInterface); if (K9.DEBUG) Log.d(K9.LOG_TAG, ">>> New WebDavTransport creation complete"); diff --git a/src/com/fsck/k9/preferences/SettingsExporter.java b/src/com/fsck/k9/preferences/SettingsExporter.java index 6085f3014..f49ec7a20 100644 --- a/src/com/fsck/k9/preferences/SettingsExporter.java +++ b/src/com/fsck/k9/preferences/SettingsExporter.java @@ -25,9 +25,9 @@ import android.util.Xml; import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.Preferences; -import com.fsck.k9.mail.Store; import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.Transport; +import com.fsck.k9.mail.store.RemoteStore; import com.fsck.k9.preferences.Settings.InvalidSettingValueException; import com.fsck.k9.preferences.Settings.SettingsDescription; @@ -223,7 +223,7 @@ public class SettingsExporter { // Write incoming server settings - ServerSettings incoming = Store.decodeStoreUri(account.getStoreUri()); + ServerSettings incoming = RemoteStore.decodeStoreUri(account.getStoreUri()); serializer.startTag(null, INCOMING_SERVER_ELEMENT); serializer.attribute(null, TYPE_ATTRIBUTE, incoming.type); diff --git a/src/com/fsck/k9/preferences/SettingsImporter.java b/src/com/fsck/k9/preferences/SettingsImporter.java index b3143f724..1a7af8d26 100644 --- a/src/com/fsck/k9/preferences/SettingsImporter.java +++ b/src/com/fsck/k9/preferences/SettingsImporter.java @@ -26,8 +26,8 @@ import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.AuthType; import com.fsck.k9.mail.ConnectionSecurity; import com.fsck.k9.mail.ServerSettings; -import com.fsck.k9.mail.Store; import com.fsck.k9.mail.Transport; +import com.fsck.k9.mail.store.RemoteStore; import com.fsck.k9.mail.store.WebDavStore; import com.fsck.k9.preferences.Settings.InvalidSettingValueException; @@ -376,7 +376,7 @@ public class SettingsImporter { // Write incoming server settings (storeUri) ServerSettings incoming = new ImportedServerSettings(account.incoming); - String storeUri = Store.createStoreUri(incoming); + String storeUri = RemoteStore.createStoreUri(incoming); putString(editor, accountKeyPrefix + Account.STORE_URI_KEY, Utility.base64Encode(storeUri)); // Mark account as disabled if the AuthType isn't EXTERNAL and the diff --git a/src/com/fsck/k9/provider/AttachmentProvider.java b/src/com/fsck/k9/provider/AttachmentProvider.java index 6f5bf32ac..bfcdaac9b 100644 --- a/src/com/fsck/k9/provider/AttachmentProvider.java +++ b/src/com/fsck/k9/provider/AttachmentProvider.java @@ -215,7 +215,7 @@ public class AttachmentProvider extends ContentProvider { final AttachmentInfo attachmentInfo; try { final Account account = Preferences.getPreferences(getContext()).getAccount(dbName); - attachmentInfo = LocalStore.getLocalInstance(account, K9.app).getAttachmentInfo(id); + attachmentInfo = LocalStore.getInstance(account, K9.app).getAttachmentInfo(id); } catch (MessagingException e) { Log.e(K9.LOG_TAG, "Unable to retrieve attachment info from local store for ID: " + id, e); return null; @@ -269,7 +269,7 @@ public class AttachmentProvider extends ContentProvider { final Account account = Preferences.getPreferences(getContext()).getAccount(dbName); try { - final LocalStore localStore = LocalStore.getLocalInstance(account, K9.app); + final LocalStore localStore = LocalStore.getInstance(account, K9.app); AttachmentInfo attachmentInfo = localStore.getAttachmentInfo(id); if (FORMAT_VIEW.equals(format) && mimeType != null) { diff --git a/src/com/fsck/k9/provider/MessageProvider.java b/src/com/fsck/k9/provider/MessageProvider.java index 6ce34baf3..c75f017df 100644 --- a/src/com/fsck/k9/provider/MessageProvider.java +++ b/src/com/fsck/k9/provider/MessageProvider.java @@ -32,6 +32,7 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.store.local.LocalFolder; import com.fsck.k9.mail.store.local.LocalMessage; import com.fsck.k9.mail.store.local.LocalStore; import com.fsck.k9.search.SearchAccount; @@ -200,9 +201,9 @@ public class MessageProvider extends ContentProvider { public static class DeleteUriExtractor implements FieldExtractor { @Override public String getField(final MessageInfoHolder source) { - final Message message = source.message; + final LocalMessage message = source.message; return CONTENT_URI + "/delete_message/" - + message.getFolder().getAccount().getAccountNumber() + "/" + + message.getAccount().getAccountNumber() + "/" + message.getFolder().getName() + "/" + message.getUid(); } } @@ -221,21 +222,21 @@ public class MessageProvider extends ContentProvider { public static class AccountExtractor implements FieldExtractor { @Override public String getField(final MessageInfoHolder source) { - return source.message.getFolder().getAccount().getDescription(); + return source.message.getAccount().getDescription(); } } public static class AccountColorExtractor implements FieldExtractor { @Override public Integer getField(final MessageInfoHolder source) { - return source.message.getFolder().getAccount().getChipColor(); + return source.message.getAccount().getChipColor(); } } public static class AccountNumberExtractor implements FieldExtractor { @Override public Integer getField(final MessageInfoHolder source) { - return source.message.getFolder().getAccount().getAccountNumber(); + return source.message.getAccount().getAccountNumber(); } } @@ -910,18 +911,18 @@ public class MessageProvider extends ContentProvider { @Override public void listLocalMessagesAddMessages(final Account account, - final String folderName, final List messages) { + final String folderName, final List messages) { // cache fields into local variables for faster access on JVM without JIT final MessageHelper helper = mMessageHelper; final List holders = mHolders; final Context context = getContext(); - for (final Message message : messages) { + for (final LocalMessage message : messages) { final MessageInfoHolder messageInfoHolder = new MessageInfoHolder(); final Folder messageFolder = message.getFolder(); - final Account messageAccount = messageFolder.getAccount(); + final Account messageAccount = messageInfoHolder.message.getAccount(); helper.populate(messageInfoHolder, message, new FolderInfoHolder(context, messageFolder, messageAccount), messageAccount); @@ -1038,9 +1039,9 @@ public class MessageProvider extends ContentProvider { } // get localstore parameter - Message msg = null; + LocalMessage msg = null; try { - Folder lf = LocalStore.getLocalInstance(myAccount, K9.app).getFolder(folderName); + LocalFolder lf = LocalStore.getInstance(myAccount, K9.app).getFolder(folderName); int msgCount = lf.getMessageCount(); if (K9.DEBUG) { Log.d(K9.LOG_TAG, "folder msg count = " + msgCount); diff --git a/src/com/fsck/k9/service/NotificationActionService.java b/src/com/fsck/k9/service/NotificationActionService.java index 107bd0f4c..2baef7560 100644 --- a/src/com/fsck/k9/service/NotificationActionService.java +++ b/src/com/fsck/k9/service/NotificationActionService.java @@ -10,9 +10,9 @@ import com.fsck.k9.Preferences; import com.fsck.k9.activity.MessageCompose; import com.fsck.k9.activity.MessageReference; import com.fsck.k9.controller.MessagingController; -import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.store.local.LocalMessage; import android.app.PendingIntent; import android.content.Context; @@ -89,10 +89,10 @@ public class NotificationActionService extends CoreService { List refs = intent.getParcelableArrayListExtra(EXTRA_MESSAGE_LIST); - List messages = new ArrayList(); + List messages = new ArrayList(); for (MessageReference ref : refs) { - Message m = ref.restoreToLocalMessage(this); + LocalMessage m = ref.restoreToLocalMessage(this); if (m != null) { messages.add(m); } @@ -106,7 +106,7 @@ public class NotificationActionService extends CoreService { MessageReference ref = (MessageReference) intent.getParcelableExtra(EXTRA_MESSAGE); Message message = ref.restoreToLocalMessage(this); if (message != null) { - Intent i = MessageCompose.getActionReplyIntent(this, account, message, false, null); + Intent i = MessageCompose.getActionReplyIntent(this, message, false, null); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(i); } else { diff --git a/tests/src/com/fsck/k9/mail/store/ImapStoreUriTest.java b/tests/src/com/fsck/k9/mail/store/ImapStoreUriTest.java index 92ef27dda..7badc97e0 100644 --- a/tests/src/com/fsck/k9/mail/store/ImapStoreUriTest.java +++ b/tests/src/com/fsck/k9/mail/store/ImapStoreUriTest.java @@ -6,14 +6,13 @@ import java.util.Map; import com.fsck.k9.mail.AuthType; import com.fsck.k9.mail.ConnectionSecurity; import com.fsck.k9.mail.ServerSettings; -import com.fsck.k9.mail.Store; -import com.fsck.k9.mail.store.ImapStore; + import junit.framework.TestCase; public class ImapStoreUriTest extends TestCase { public void testDecodeStoreUriImapAllExtras() { String uri = "imap://PLAIN:user:pass@server:143/0%7CcustomPathPrefix"; - ServerSettings settings = Store.decodeStoreUri(uri); + ServerSettings settings = RemoteStore.decodeStoreUri(uri); assertEquals(AuthType.PLAIN, settings.authenticationType); assertEquals("user", settings.username); @@ -26,7 +25,7 @@ public class ImapStoreUriTest extends TestCase { public void testDecodeStoreUriImapNoExtras() { String uri = "imap://PLAIN:user:pass@server:143/"; - ServerSettings settings = Store.decodeStoreUri(uri); + ServerSettings settings = RemoteStore.decodeStoreUri(uri); assertEquals(AuthType.PLAIN, settings.authenticationType); assertEquals("user", settings.username); @@ -38,7 +37,7 @@ public class ImapStoreUriTest extends TestCase { public void testDecodeStoreUriImapPrefixOnly() { String uri = "imap://PLAIN:user:pass@server:143/customPathPrefix"; - ServerSettings settings = Store.decodeStoreUri(uri); + ServerSettings settings = RemoteStore.decodeStoreUri(uri); assertEquals(AuthType.PLAIN, settings.authenticationType); assertEquals("user", settings.username); @@ -51,7 +50,7 @@ public class ImapStoreUriTest extends TestCase { public void testDecodeStoreUriImapEmptyPrefix() { String uri = "imap://PLAIN:user:pass@server:143/0%7C"; - ServerSettings settings = Store.decodeStoreUri(uri); + ServerSettings settings = RemoteStore.decodeStoreUri(uri); assertEquals(AuthType.PLAIN, settings.authenticationType); assertEquals("user", settings.username); @@ -64,7 +63,7 @@ public class ImapStoreUriTest extends TestCase { public void testDecodeStoreUriImapAutodetectAndPrefix() { String uri = "imap://PLAIN:user:pass@server:143/1%7CcustomPathPrefix"; - ServerSettings settings = Store.decodeStoreUri(uri); + ServerSettings settings = RemoteStore.decodeStoreUri(uri); assertEquals(AuthType.PLAIN, settings.authenticationType); assertEquals("user", settings.username); @@ -84,7 +83,7 @@ public class ImapStoreUriTest extends TestCase { ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143, ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", null, extra); - String uri = Store.createStoreUri(settings); + String uri = RemoteStore.createStoreUri(settings); assertEquals("imap://PLAIN:user:pass@server:143/0%7CcustomPathPrefix", uri); } @@ -97,7 +96,7 @@ public class ImapStoreUriTest extends TestCase { ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143, ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", null, extra); - String uri = Store.createStoreUri(settings); + String uri = RemoteStore.createStoreUri(settings); assertEquals("imap://PLAIN:user:pass@server:143/0%7C", uri); } @@ -106,7 +105,7 @@ public class ImapStoreUriTest extends TestCase { ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143, ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", null); - String uri = Store.createStoreUri(settings); + String uri = RemoteStore.createStoreUri(settings); assertEquals("imap://PLAIN:user:pass@server:143/1%7C", uri); } @@ -118,7 +117,7 @@ public class ImapStoreUriTest extends TestCase { ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143, ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", null, extra); - String uri = Store.createStoreUri(settings); + String uri = RemoteStore.createStoreUri(settings); assertEquals("imap://PLAIN:user:pass@server:143/1%7C", uri); } From 5af649c27164df1b3347b2f218ee48909baf2a74 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Fri, 12 Dec 2014 06:54:34 +0000 Subject: [PATCH 02/29] Avoid cast --- src/com/fsck/k9/mail/store/local/LocalStore.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/fsck/k9/mail/store/local/LocalStore.java b/src/com/fsck/k9/mail/store/local/LocalStore.java index f4c82743a..1aad9386a 100644 --- a/src/com/fsck/k9/mail/store/local/LocalStore.java +++ b/src/com/fsck/k9/mail/store/local/LocalStore.java @@ -70,7 +70,7 @@ public class LocalStore extends Store implements Serializable { /** * Local stores indexed by UUID because the Uri may change due to migration to/from SD-card. */ - private static ConcurrentMap sLocalStores = new ConcurrentHashMap(); + private static ConcurrentMap sLocalStores = new ConcurrentHashMap(); /* * a String containing the columns getMessages expects to work with @@ -195,7 +195,7 @@ public class LocalStore extends Store implements Serializable { // Use per-account locks so DatabaseUpgradeService always knows which account database is // currently upgraded. synchronized (lock) { - Store store = sLocalStores.get(accountUuid); + LocalStore store = sLocalStores.get(accountUuid); if (store == null) { // Creating a LocalStore instance will create or upgrade the database if @@ -205,7 +205,7 @@ public class LocalStore extends Store implements Serializable { sLocalStores.put(accountUuid, store); } - return (LocalStore) store; + return store; } } From 25367197490dd6e2477b067e2f185f878a980d49 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Fri, 12 Dec 2014 07:03:14 +0000 Subject: [PATCH 03/29] Naming --- src/com/fsck/k9/Account.java | 24 ++++++++++----------- src/com/fsck/k9/mail/store/StoreConfig.java | 11 +++++----- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/com/fsck/k9/Account.java b/src/com/fsck/k9/Account.java index d7413b6d1..ac61af945 100644 --- a/src/com/fsck/k9/Account.java +++ b/src/com/fsck/k9/Account.java @@ -1078,8 +1078,8 @@ public class Account implements BaseAccount, StoreConfig { return mDraftsFolderName; } - public synchronized void setDraftsFolderName(String draftsFolderName) { - mDraftsFolderName = draftsFolderName; + public synchronized void setDraftsFolderName(String name) { + mDraftsFolderName = name; } /** @@ -1098,8 +1098,8 @@ public class Account implements BaseAccount, StoreConfig { return K9.ERROR_FOLDER_NAME; } - public synchronized void setSentFolderName(String sentFolderName) { - mSentFolderName = sentFolderName; + public synchronized void setSentFolderName(String name) { + mSentFolderName = name; } /** @@ -1115,8 +1115,8 @@ public class Account implements BaseAccount, StoreConfig { return mTrashFolderName; } - public synchronized void setTrashFolderName(String trashFolderName) { - mTrashFolderName = trashFolderName; + public synchronized void setTrashFolderName(String name) { + mTrashFolderName = name; } /** @@ -1147,8 +1147,8 @@ public class Account implements BaseAccount, StoreConfig { return mSpamFolderName; } - public synchronized void setSpamFolderName(String spamFolderName) { - mSpamFolderName = spamFolderName; + public synchronized void setSpamFolderName(String name) { + mSpamFolderName = name; } /** @@ -1167,8 +1167,8 @@ public class Account implements BaseAccount, StoreConfig { return mAutoExpandFolderName; } - public synchronized void setAutoExpandFolderName(String autoExpandFolderName) { - mAutoExpandFolderName = autoExpandFolderName; + public synchronized void setAutoExpandFolderName(String name) { + mAutoExpandFolderName = name; } public synchronized int getAccountNumber() { @@ -1659,8 +1659,8 @@ public class Account implements BaseAccount, StoreConfig { return mInboxFolderName; } - public void setInboxFolderName(String mInboxFolderName) { - this.mInboxFolderName = mInboxFolderName; + public void setInboxFolderName(String name) { + this.mInboxFolderName = name; } public synchronized boolean syncRemoteDeletions() { diff --git a/src/com/fsck/k9/mail/store/StoreConfig.java b/src/com/fsck/k9/mail/store/StoreConfig.java index ba83ba201..23d177d90 100644 --- a/src/com/fsck/k9/mail/store/StoreConfig.java +++ b/src/com/fsck/k9/mail/store/StoreConfig.java @@ -12,12 +12,11 @@ public interface StoreConfig { String getOutboxFolderName(); String getDraftsFolderName(); - void setInboxFolderName(String folderName); - void setDraftsFolderName(String decodedFolderName); - void setTrashFolderName(String decodedFolderName); - void setSpamFolderName(String decodedFolderName); - void setSentFolderName(String decodedFolderName); - void setAutoExpandFolderName(String folderName); + void setDraftsFolderName(String name); + void setTrashFolderName(String name); + void setSpamFolderName(String name); + void setSentFolderName(String name); + void setAutoExpandFolderName(String name); int getMaximumAutoDownloadMessageSize(); From 9f16b9f465735520f31753f7bb0f9d5075fe0ba2 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Fri, 12 Dec 2014 12:34:57 +0000 Subject: [PATCH 04/29] Move SSL code into package --- src/com/fsck/k9/Preferences.java | 1 - .../{net => mail}/ssl/KeyChainKeyManager.java | 4 +- .../ssl/TrustManagerFactory.java | 2 +- .../ssl/TrustedSocketFactory.java | 2 +- src/com/fsck/k9/mail/store/ImapStore.java | 2 +- src/com/fsck/k9/mail/store/Pop3Store.java | 2 +- .../k9/mail/store/WebDavSocketFactory.java | 2 +- .../fsck/k9/mail/store/local/LocalStore.java | 79 +++++++++---------- .../fsck/k9/mail/transport/SmtpTransport.java | 2 +- .../ssl/TrustManagerFactoryTest.java | 3 +- 10 files changed, 49 insertions(+), 50 deletions(-) rename src/com/fsck/k9/{net => mail}/ssl/KeyChainKeyManager.java (98%) rename src/com/fsck/k9/{net => mail}/ssl/TrustManagerFactory.java (99%) rename src/com/fsck/k9/{net => mail}/ssl/TrustedSocketFactory.java (99%) rename tests/src/com/fsck/k9/{net => mail}/ssl/TrustManagerFactoryTest.java (99%) diff --git a/src/com/fsck/k9/Preferences.java b/src/com/fsck/k9/Preferences.java index 6504626a2..04754af97 100644 --- a/src/com/fsck/k9/Preferences.java +++ b/src/com/fsck/k9/Preferences.java @@ -13,7 +13,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.util.Log; -import com.fsck.k9.mail.Store; import com.fsck.k9.mail.store.local.LocalStore; import com.fsck.k9.preferences.Editor; import com.fsck.k9.preferences.Storage; diff --git a/src/com/fsck/k9/net/ssl/KeyChainKeyManager.java b/src/com/fsck/k9/mail/ssl/KeyChainKeyManager.java similarity index 98% rename from src/com/fsck/k9/net/ssl/KeyChainKeyManager.java rename to src/com/fsck/k9/mail/ssl/KeyChainKeyManager.java index c15fc58ad..52973e28b 100644 --- a/src/com/fsck/k9/net/ssl/KeyChainKeyManager.java +++ b/src/com/fsck/k9/mail/ssl/KeyChainKeyManager.java @@ -1,5 +1,5 @@ -package com.fsck.k9.net.ssl; +package com.fsck.k9.mail.ssl; import java.net.Socket; import java.security.Principal; @@ -29,7 +29,7 @@ import com.fsck.k9.mail.MessagingException; * For client certificate authentication! Provide private keys and certificates * during the TLS handshake using the Android 4.0 KeyChain API. */ -public class KeyChainKeyManager extends X509ExtendedKeyManager { +class KeyChainKeyManager extends X509ExtendedKeyManager { private static PrivateKey sClientCertificateReferenceWorkaround; diff --git a/src/com/fsck/k9/net/ssl/TrustManagerFactory.java b/src/com/fsck/k9/mail/ssl/TrustManagerFactory.java similarity index 99% rename from src/com/fsck/k9/net/ssl/TrustManagerFactory.java rename to src/com/fsck/k9/mail/ssl/TrustManagerFactory.java index 4e20f5c67..c1956011c 100644 --- a/src/com/fsck/k9/net/ssl/TrustManagerFactory.java +++ b/src/com/fsck/k9/mail/ssl/TrustManagerFactory.java @@ -1,5 +1,5 @@ -package com.fsck.k9.net.ssl; +package com.fsck.k9.mail.ssl; import android.util.Log; diff --git a/src/com/fsck/k9/net/ssl/TrustedSocketFactory.java b/src/com/fsck/k9/mail/ssl/TrustedSocketFactory.java similarity index 99% rename from src/com/fsck/k9/net/ssl/TrustedSocketFactory.java rename to src/com/fsck/k9/mail/ssl/TrustedSocketFactory.java index 12188f957..9d68dd205 100644 --- a/src/com/fsck/k9/net/ssl/TrustedSocketFactory.java +++ b/src/com/fsck/k9/mail/ssl/TrustedSocketFactory.java @@ -1,4 +1,4 @@ -package com.fsck.k9.net.ssl; +package com.fsck.k9.mail.ssl; import android.util.Log; diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java index 98c3a6833..542f78b83 100644 --- a/src/com/fsck/k9/mail/store/ImapStore.java +++ b/src/com/fsck/k9/mail/store/ImapStore.java @@ -90,7 +90,7 @@ import com.fsck.k9.mail.store.ImapResponseParser.ImapList; import com.fsck.k9.mail.store.ImapResponseParser.ImapResponse; import com.fsck.k9.mail.store.imap.ImapUtility; import com.fsck.k9.mail.transport.imap.ImapSettings; -import com.fsck.k9.net.ssl.TrustedSocketFactory; +import com.fsck.k9.mail.ssl.TrustedSocketFactory; import com.beetstra.jutf7.CharsetProvider; import com.jcraft.jzlib.JZlib; diff --git a/src/com/fsck/k9/mail/store/Pop3Store.java b/src/com/fsck/k9/mail/store/Pop3Store.java index 593191a14..e0b290c76 100644 --- a/src/com/fsck/k9/mail/store/Pop3Store.java +++ b/src/com/fsck/k9/mail/store/Pop3Store.java @@ -12,7 +12,7 @@ import com.fsck.k9.mail.*; import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.filter.Hex; import com.fsck.k9.mail.internet.MimeMessage; -import com.fsck.k9.net.ssl.TrustedSocketFactory; +import com.fsck.k9.mail.ssl.TrustedSocketFactory; import javax.net.ssl.SSLException; diff --git a/src/com/fsck/k9/mail/store/WebDavSocketFactory.java b/src/com/fsck/k9/mail/store/WebDavSocketFactory.java index 3557002a2..5345b41fd 100644 --- a/src/com/fsck/k9/mail/store/WebDavSocketFactory.java +++ b/src/com/fsck/k9/mail/store/WebDavSocketFactory.java @@ -4,7 +4,7 @@ import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.scheme.LayeredSocketFactory; import org.apache.http.params.HttpParams; -import com.fsck.k9.net.ssl.TrustManagerFactory; +import com.fsck.k9.mail.ssl.TrustManagerFactory; import java.io.IOException; import java.net.InetAddress; diff --git a/src/com/fsck/k9/mail/store/local/LocalStore.java b/src/com/fsck/k9/mail/store/local/LocalStore.java index 1aad9386a..0dded3e3f 100644 --- a/src/com/fsck/k9/mail/store/local/LocalStore.java +++ b/src/com/fsck/k9/mail/store/local/LocalStore.java @@ -1,6 +1,40 @@ package com.fsck.k9.mail.store.local; +import android.app.Application; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.net.Uri; +import android.text.TextUtils; +import android.util.Log; +import com.fsck.k9.Account; +import com.fsck.k9.K9; +import com.fsck.k9.Preferences; +import com.fsck.k9.controller.MessageRetrievalListener; +import com.fsck.k9.helper.UrlEncodingHelper; +import com.fsck.k9.helper.Utility; +import com.fsck.k9.mail.Flag; +import com.fsck.k9.mail.Folder; +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.Store; +import com.fsck.k9.mail.store.RemoteStore; +import com.fsck.k9.mail.store.StorageManager; +import com.fsck.k9.mail.store.StorageManager.StorageProvider; +import com.fsck.k9.mail.store.StoreConfig; +import com.fsck.k9.mail.store.UnavailableStorageException; +import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback; +import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException; +import com.fsck.k9.provider.EmailProvider; +import com.fsck.k9.provider.EmailProvider.MessageColumns; +import com.fsck.k9.search.LocalSearch; +import com.fsck.k9.search.SearchSpecification.Attribute; +import com.fsck.k9.search.SearchSpecification.Searchfield; +import com.fsck.k9.search.SqlQueryBuilder; + import java.io.File; import java.io.Serializable; import java.util.ArrayList; @@ -13,41 +47,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import android.app.Application; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.net.Uri; -import android.text.TextUtils; -import android.util.Log; - -import com.fsck.k9.Account; -import com.fsck.k9.K9; -import com.fsck.k9.Preferences; -import com.fsck.k9.controller.MessageRetrievalListener; -import com.fsck.k9.helper.UrlEncodingHelper; -import com.fsck.k9.helper.Utility; -import com.fsck.k9.mail.Flag; -import com.fsck.k9.mail.Folder; -import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.mail.Store; -import com.fsck.k9.mail.store.RemoteStore; -import com.fsck.k9.mail.store.StoreConfig; -import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback; -import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException; -import com.fsck.k9.mail.store.StorageManager; -import com.fsck.k9.mail.store.StorageManager.StorageProvider; -import com.fsck.k9.mail.store.UnavailableStorageException; -import com.fsck.k9.provider.EmailProvider; -import com.fsck.k9.provider.EmailProvider.MessageColumns; -import com.fsck.k9.search.LocalSearch; -import com.fsck.k9.search.SearchSpecification.Attribute; -import com.fsck.k9.search.SearchSpecification.Searchfield; -import com.fsck.k9.search.SqlQueryBuilder; - /** *
  * Implements a SQLite database backed local store for Messages.
@@ -209,17 +208,17 @@ public class LocalStore extends Store implements Serializable {
         }
     }
 
-    public static void removeAccount(StoreConfig storeConfig) {
+    public static void removeAccount(Account account) {
         try {
-            RemoteStore.removeInstance(storeConfig);
+            RemoteStore.removeInstance(account);
         } catch (Exception e) {
-            Log.e(K9.LOG_TAG, "Failed to reset remote store for account " + storeConfig.getUuid(), e);
+            Log.e(K9.LOG_TAG, "Failed to reset remote store for account " + account.getUuid(), e);
         }
 
         try {
-            removeInstance(storeConfig);
+            removeInstance(account);
         } catch (Exception e) {
-            Log.e(K9.LOG_TAG, "Failed to reset local store for account " + storeConfig.getUuid(), e);
+            Log.e(K9.LOG_TAG, "Failed to reset local store for account " + account.getUuid(), e);
         }
     }
 
diff --git a/src/com/fsck/k9/mail/transport/SmtpTransport.java b/src/com/fsck/k9/mail/transport/SmtpTransport.java
index a5365a33a..4e7959f7f 100644
--- a/src/com/fsck/k9/mail/transport/SmtpTransport.java
+++ b/src/com/fsck/k9/mail/transport/SmtpTransport.java
@@ -17,7 +17,7 @@ import com.fsck.k9.mail.filter.SmtpDataStuffing;
 import com.fsck.k9.mail.internet.MimeUtility;
 import com.fsck.k9.mail.store.StoreConfig;
 import com.fsck.k9.mail.store.local.LocalMessage;
-import com.fsck.k9.net.ssl.TrustedSocketFactory;
+import com.fsck.k9.mail.ssl.TrustedSocketFactory;
 
 import javax.net.ssl.SSLException;
 
diff --git a/tests/src/com/fsck/k9/net/ssl/TrustManagerFactoryTest.java b/tests/src/com/fsck/k9/mail/ssl/TrustManagerFactoryTest.java
similarity index 99%
rename from tests/src/com/fsck/k9/net/ssl/TrustManagerFactoryTest.java
rename to tests/src/com/fsck/k9/mail/ssl/TrustManagerFactoryTest.java
index 45efc0177..14da17b7f 100644
--- a/tests/src/com/fsck/k9/net/ssl/TrustManagerFactoryTest.java
+++ b/tests/src/com/fsck/k9/mail/ssl/TrustManagerFactoryTest.java
@@ -1,7 +1,8 @@
-package com.fsck.k9.net.ssl;
+package com.fsck.k9.mail.ssl;
 
 import javax.net.ssl.X509TrustManager;
 
+import com.fsck.k9.mail.ssl.TrustManagerFactory;
 import com.fsck.k9.security.LocalKeyStore;
 
 import java.io.ByteArrayInputStream;

From 6264527abc61358f1f485f950606e9da33e1534b Mon Sep 17 00:00:00 2001
From: Jan Berkel 
Date: Fri, 12 Dec 2014 12:42:48 +0000
Subject: [PATCH 05/29] Remove Preferences dependency

---
 src/com/fsck/k9/activity/ChooseFolder.java    | 24 +++++++------------
 src/com/fsck/k9/activity/FolderList.java      | 20 ++++++----------
 .../k9/controller/MessagingController.java    |  3 +--
 src/com/fsck/k9/mail/Folder.java              |  5 ----
 src/com/fsck/k9/mail/store/StoreConfig.java   |  1 +
 5 files changed, 18 insertions(+), 35 deletions(-)

diff --git a/src/com/fsck/k9/activity/ChooseFolder.java b/src/com/fsck/k9/activity/ChooseFolder.java
index 83e8070b8..91430796d 100644
--- a/src/com/fsck/k9/activity/ChooseFolder.java
+++ b/src/com/fsck/k9/activity/ChooseFolder.java
@@ -280,22 +280,16 @@ public class ChooseFolder extends K9ListActivity {
                         mAccount.getInboxFolderName().equalsIgnoreCase(name)))) {
                     continue;
                 }
-                try {
-                    folder.refresh(prefs);
-                    Folder.FolderClass fMode = folder.getDisplayClass();
+                Folder.FolderClass fMode = folder.getDisplayClass();
 
-                    if ((aMode == Account.FolderMode.FIRST_CLASS &&
-                            fMode != Folder.FolderClass.FIRST_CLASS) || (
-                                aMode == Account.FolderMode.FIRST_AND_SECOND_CLASS &&
-                                fMode != Folder.FolderClass.FIRST_CLASS &&
-                                fMode != Folder.FolderClass.SECOND_CLASS) || (
-                                aMode == Account.FolderMode.NOT_SECOND_CLASS &&
-                                fMode == Folder.FolderClass.SECOND_CLASS)) {
-                        continue;
-                    }
-                } catch (MessagingException me) {
-                    Log.e(K9.LOG_TAG, "Couldn't get prefs to check for displayability of folder " +
-                            folder.getName(), me);
+                if ((aMode == FolderMode.FIRST_CLASS &&
+                        fMode != Folder.FolderClass.FIRST_CLASS) || (
+                            aMode == FolderMode.FIRST_AND_SECOND_CLASS &&
+                            fMode != Folder.FolderClass.FIRST_CLASS &&
+                            fMode != Folder.FolderClass.SECOND_CLASS) || (
+                            aMode == FolderMode.NOT_SECOND_CLASS &&
+                            fMode == Folder.FolderClass.SECOND_CLASS)) {
+                    continue;
                 }
 
                 if (folder.isInTopGroup()) {
diff --git a/src/com/fsck/k9/activity/FolderList.java b/src/com/fsck/k9/activity/FolderList.java
index 670419cf1..2316b364f 100644
--- a/src/com/fsck/k9/activity/FolderList.java
+++ b/src/com/fsck/k9/activity/FolderList.java
@@ -749,20 +749,14 @@ public class FolderList extends K9ListActivity {
                     Account.FolderMode aMode = account.getFolderDisplayMode();
                     Preferences prefs = Preferences.getPreferences(getApplication().getApplicationContext());
                     for (Folder folder : folders) {
-                        try {
-                            folder.refresh(prefs);
+                        Folder.FolderClass fMode = folder.getDisplayClass();
 
-                            Folder.FolderClass fMode = folder.getDisplayClass();
-
-                            if ((aMode == Account.FolderMode.FIRST_CLASS && fMode != Folder.FolderClass.FIRST_CLASS)
-                                    || (aMode == Account.FolderMode.FIRST_AND_SECOND_CLASS &&
-                                        fMode != Folder.FolderClass.FIRST_CLASS &&
-                                        fMode != Folder.FolderClass.SECOND_CLASS)
-                            || (aMode == Account.FolderMode.NOT_SECOND_CLASS && fMode == Folder.FolderClass.SECOND_CLASS)) {
-                                continue;
-                            }
-                        } catch (MessagingException me) {
-                            Log.e(K9.LOG_TAG, "Couldn't get prefs to check for displayability of folder " + folder.getName(), me);
+                        if ((aMode == FolderMode.FIRST_CLASS && fMode != Folder.FolderClass.FIRST_CLASS)
+                                || (aMode == FolderMode.FIRST_AND_SECOND_CLASS &&
+                                    fMode != Folder.FolderClass.FIRST_CLASS &&
+                                    fMode != Folder.FolderClass.SECOND_CLASS)
+                        || (aMode == FolderMode.NOT_SECOND_CLASS && fMode == Folder.FolderClass.SECOND_CLASS)) {
+                            continue;
                         }
 
                         FolderInfoHolder holder = null;
diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java
index d776c5fb3..e8cbb048a 100644
--- a/src/com/fsck/k9/controller/MessagingController.java
+++ b/src/com/fsck/k9/controller/MessagingController.java
@@ -694,6 +694,7 @@ public class MessagingController implements Runnable {
                 @Override
                 public void messagesFinished(int number) {}
                 @Override
+
                 public void messageFinished(LocalMessage message, int number, int ofTotal) {
                     if (!isMessageSuppressed(message)) {
                         List messages = new ArrayList();
@@ -4391,7 +4392,6 @@ public class MessagingController implements Runnable {
             Store localStore = account.getLocalStore();
             for (final Folder folder : localStore.getPersonalNamespaces(false)) {
                 folder.open(Folder.OPEN_MODE_RW);
-                folder.refresh(prefs);
 
                 Folder.FolderClass fDisplayClass = folder.getDisplayClass();
                 Folder.FolderClass fSyncClass = folder.getSyncClass();
@@ -5305,7 +5305,6 @@ public class MessagingController implements Runnable {
                     continue;
                 }
                 folder.open(Folder.OPEN_MODE_RW);
-                folder.refresh(prefs);
 
                 Folder.FolderClass fDisplayClass = folder.getDisplayClass();
                 Folder.FolderClass fPushClass = folder.getPushClass();
diff --git a/src/com/fsck/k9/mail/Folder.java b/src/com/fsck/k9/mail/Folder.java
index 5669e9794..d8bf087a6 100644
--- a/src/com/fsck/k9/mail/Folder.java
+++ b/src/com/fsck/k9/mail/Folder.java
@@ -8,7 +8,6 @@ import java.util.Set;
 import android.util.Log;
 
 import com.fsck.k9.K9;
-import com.fsck.k9.Preferences;
 import com.fsck.k9.controller.MessageRetrievalListener;
 
 public abstract class Folder {
@@ -217,10 +216,6 @@ public abstract class Folder {
         return getSyncClass();
     }
 
-    public void refresh(Preferences preferences) throws MessagingException {
-
-    }
-
     public boolean isInTopGroup() {
         return false;
     }
diff --git a/src/com/fsck/k9/mail/store/StoreConfig.java b/src/com/fsck/k9/mail/store/StoreConfig.java
index 23d177d90..12f7a025d 100644
--- a/src/com/fsck/k9/mail/store/StoreConfig.java
+++ b/src/com/fsck/k9/mail/store/StoreConfig.java
@@ -17,6 +17,7 @@ public interface StoreConfig {
     void setSpamFolderName(String name);
     void setSentFolderName(String name);
     void setAutoExpandFolderName(String name);
+    void setInboxFolderName(String name);
 
     int getMaximumAutoDownloadMessageSize();
 

From 708fb57c0462ca62feae824b36126522ad3124c6 Mon Sep 17 00:00:00 2001
From: Jan Berkel 
Date: Fri, 12 Dec 2014 12:49:26 +0000
Subject: [PATCH 06/29] Move things into local

---
 src/com/fsck/k9/controller/MessagingController.java             | 2 ++
 src/com/fsck/k9/mail/Folder.java                                | 2 +-
 src/com/fsck/k9/mail/store/ImapStore.java                       | 2 +-
 src/com/fsck/k9/mail/store/Pop3Store.java                       | 2 +-
 src/com/fsck/k9/mail/store/WebDavStore.java                     | 2 +-
 src/com/fsck/k9/mail/store/local/LocalFolder.java               | 2 --
 src/com/fsck/k9/mail/store/local/LocalStore.java                | 1 -
 .../store/local}/MessageRemovalListener.java                    | 2 +-
 .../store/local}/MessageRetrievalListener.java                  | 2 +-
 9 files changed, 8 insertions(+), 9 deletions(-)
 rename src/com/fsck/k9/{controller => mail/store/local}/MessageRemovalListener.java (77%)
 rename src/com/fsck/k9/{controller => mail/store/local}/MessageRetrievalListener.java (92%)

diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java
index e8cbb048a..458c713c7 100644
--- a/src/com/fsck/k9/controller/MessagingController.java
+++ b/src/com/fsck/k9/controller/MessagingController.java
@@ -81,6 +81,8 @@ import com.fsck.k9.mail.Transport;
 import com.fsck.k9.mail.internet.MimeMessage;
 import com.fsck.k9.mail.internet.MimeUtility;
 import com.fsck.k9.mail.internet.TextBody;
+import com.fsck.k9.mail.store.local.MessageRemovalListener;
+import com.fsck.k9.mail.store.local.MessageRetrievalListener;
 import com.fsck.k9.mail.store.local.LocalFolder;
 import com.fsck.k9.mail.store.local.LocalMessage;
 import com.fsck.k9.mail.store.local.LocalStore;
diff --git a/src/com/fsck/k9/mail/Folder.java b/src/com/fsck/k9/mail/Folder.java
index d8bf087a6..c4f1a3998 100644
--- a/src/com/fsck/k9/mail/Folder.java
+++ b/src/com/fsck/k9/mail/Folder.java
@@ -8,7 +8,7 @@ import java.util.Set;
 import android.util.Log;
 
 import com.fsck.k9.K9;
-import com.fsck.k9.controller.MessageRetrievalListener;
+import com.fsck.k9.mail.store.local.MessageRetrievalListener;
 
 public abstract class Folder {
     private String status = null;
diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java
index 542f78b83..b8be10039 100644
--- a/src/com/fsck/k9/mail/store/ImapStore.java
+++ b/src/com/fsck/k9/mail/store/ImapStore.java
@@ -57,7 +57,6 @@ import android.util.Log;
 
 import com.fsck.k9.K9;
 import com.fsck.k9.R;
-import com.fsck.k9.controller.MessageRetrievalListener;
 import com.fsck.k9.helper.UrlEncodingHelper;
 import com.fsck.k9.helper.Utility;
 import com.fsck.k9.helper.power.TracingPowerManager;
@@ -72,6 +71,7 @@ import com.fsck.k9.mail.FetchProfile;
 import com.fsck.k9.mail.Flag;
 import com.fsck.k9.mail.Folder;
 import com.fsck.k9.mail.Message;
+import com.fsck.k9.mail.store.local.MessageRetrievalListener;
 import com.fsck.k9.mail.MessagingException;
 import com.fsck.k9.mail.Part;
 import com.fsck.k9.mail.PushReceiver;
diff --git a/src/com/fsck/k9/mail/store/Pop3Store.java b/src/com/fsck/k9/mail/store/Pop3Store.java
index e0b290c76..0871579b1 100644
--- a/src/com/fsck/k9/mail/store/Pop3Store.java
+++ b/src/com/fsck/k9/mail/store/Pop3Store.java
@@ -5,7 +5,6 @@ import android.util.Log;
 
 import com.fsck.k9.K9;
 import com.fsck.k9.R;
-import com.fsck.k9.controller.MessageRetrievalListener;
 import com.fsck.k9.helper.UrlEncodingHelper;
 import com.fsck.k9.helper.Utility;
 import com.fsck.k9.mail.*;
@@ -13,6 +12,7 @@ import com.fsck.k9.mail.filter.Base64;
 import com.fsck.k9.mail.filter.Hex;
 import com.fsck.k9.mail.internet.MimeMessage;
 import com.fsck.k9.mail.ssl.TrustedSocketFactory;
+import com.fsck.k9.mail.store.local.MessageRetrievalListener;
 
 import javax.net.ssl.SSLException;
 
diff --git a/src/com/fsck/k9/mail/store/WebDavStore.java b/src/com/fsck/k9/mail/store/WebDavStore.java
index 61dba1088..300a68d18 100644
--- a/src/com/fsck/k9/mail/store/WebDavStore.java
+++ b/src/com/fsck/k9/mail/store/WebDavStore.java
@@ -3,13 +3,13 @@ package com.fsck.k9.mail.store;
 import android.util.Log;
 
 import com.fsck.k9.K9;
-import com.fsck.k9.controller.MessageRetrievalListener;
 
 import com.fsck.k9.helper.UrlEncodingHelper;
 import com.fsck.k9.helper.Utility;
 import com.fsck.k9.mail.*;
 import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
 import com.fsck.k9.mail.internet.MimeMessage;
+import com.fsck.k9.mail.store.local.MessageRetrievalListener;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.http.*;
diff --git a/src/com/fsck/k9/mail/store/local/LocalFolder.java b/src/com/fsck/k9/mail/store/local/LocalFolder.java
index 22c3674ee..d4273d03f 100644
--- a/src/com/fsck/k9/mail/store/local/LocalFolder.java
+++ b/src/com/fsck/k9/mail/store/local/LocalFolder.java
@@ -32,8 +32,6 @@ import com.fsck.k9.Account;
 import com.fsck.k9.K9;
 import com.fsck.k9.Account.MessageFormat;
 import com.fsck.k9.activity.Search;
-import com.fsck.k9.controller.MessageRemovalListener;
-import com.fsck.k9.controller.MessageRetrievalListener;
 import com.fsck.k9.helper.HtmlConverter;
 import com.fsck.k9.helper.Utility;
 import com.fsck.k9.mail.Address;
diff --git a/src/com/fsck/k9/mail/store/local/LocalStore.java b/src/com/fsck/k9/mail/store/local/LocalStore.java
index 0dded3e3f..64bacb2ef 100644
--- a/src/com/fsck/k9/mail/store/local/LocalStore.java
+++ b/src/com/fsck/k9/mail/store/local/LocalStore.java
@@ -14,7 +14,6 @@ import android.util.Log;
 import com.fsck.k9.Account;
 import com.fsck.k9.K9;
 import com.fsck.k9.Preferences;
-import com.fsck.k9.controller.MessageRetrievalListener;
 import com.fsck.k9.helper.UrlEncodingHelper;
 import com.fsck.k9.helper.Utility;
 import com.fsck.k9.mail.Flag;
diff --git a/src/com/fsck/k9/controller/MessageRemovalListener.java b/src/com/fsck/k9/mail/store/local/MessageRemovalListener.java
similarity index 77%
rename from src/com/fsck/k9/controller/MessageRemovalListener.java
rename to src/com/fsck/k9/mail/store/local/MessageRemovalListener.java
index 38973dde4..95e35fd22 100644
--- a/src/com/fsck/k9/controller/MessageRemovalListener.java
+++ b/src/com/fsck/k9/mail/store/local/MessageRemovalListener.java
@@ -1,4 +1,4 @@
-package com.fsck.k9.controller;
+package com.fsck.k9.mail.store.local;
 
 import com.fsck.k9.mail.Message;
 
diff --git a/src/com/fsck/k9/controller/MessageRetrievalListener.java b/src/com/fsck/k9/mail/store/local/MessageRetrievalListener.java
similarity index 92%
rename from src/com/fsck/k9/controller/MessageRetrievalListener.java
rename to src/com/fsck/k9/mail/store/local/MessageRetrievalListener.java
index 6af8714af..12679b4e5 100644
--- a/src/com/fsck/k9/controller/MessageRetrievalListener.java
+++ b/src/com/fsck/k9/mail/store/local/MessageRetrievalListener.java
@@ -1,5 +1,5 @@
 
-package com.fsck.k9.controller;
+package com.fsck.k9.mail.store.local;
 
 import com.fsck.k9.mail.Message;
 

From 0024f39bc6fc7731d317bb4343b701f132b507a8 Mon Sep 17 00:00:00 2001
From: Jan Berkel 
Date: Fri, 12 Dec 2014 13:04:59 +0000
Subject: [PATCH 07/29] Local messages

---
 src/com/fsck/k9/activity/MessageCompose.java  |  6 ++--
 src/com/fsck/k9/activity/MessageList.java     | 16 ++++-----
 .../k9/controller/MessagingController.java    | 29 ++++++++--------
 .../fsck/k9/fragment/MessageListFragment.java | 28 +++++++---------
 .../fsck/k9/fragment/MessageViewFragment.java |  6 ++--
 src/com/fsck/k9/mail/Message.java             | 20 +----------
 .../fsck/k9/mail/internet/MimeMessage.java    | 33 +++++++++----------
 .../fsck/k9/mail/store/local/LocalFolder.java |  8 ++---
 .../k9/mail/store/local/LocalMessage.java     | 24 +++++++++++---
 .../k9/service/NotificationActionService.java |  4 +--
 10 files changed, 82 insertions(+), 92 deletions(-)

diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java
index 2e6bacf99..9018cba8a 100644
--- a/src/com/fsck/k9/activity/MessageCompose.java
+++ b/src/com/fsck/k9/activity/MessageCompose.java
@@ -442,7 +442,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
      */
     public static Intent getActionReplyIntent(
             Context context,
-            Message message,
+            LocalMessage message,
             boolean replyAll,
             String messageBody) {
         Intent i = new Intent(context, MessageCompose.class);
@@ -467,7 +467,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
      */
     public static void actionReply(
         Context context,
-        Message message,
+        LocalMessage message,
         boolean replyAll,
         String messageBody) {
         context.startActivity(getActionReplyIntent(context, message, replyAll, messageBody));
@@ -481,7 +481,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
      */
     public static void actionForward(
             Context context,
-            Message message,
+            LocalMessage message,
             String messageBody) {
         Intent i = new Intent(context, MessageCompose.class);
         i.putExtra(EXTRA_MESSAGE_BODY, messageBody);
diff --git a/src/com/fsck/k9/activity/MessageList.java b/src/com/fsck/k9/activity/MessageList.java
index 765d03673..604c1a166 100644
--- a/src/com/fsck/k9/activity/MessageList.java
+++ b/src/com/fsck/k9/activity/MessageList.java
@@ -42,8 +42,8 @@ import com.fsck.k9.fragment.MessageListFragment;
 import com.fsck.k9.fragment.MessageListFragment.MessageListFragmentListener;
 import com.fsck.k9.fragment.MessageViewFragment;
 import com.fsck.k9.fragment.MessageViewFragment.MessageViewFragmentListener;
-import com.fsck.k9.mail.Message;
 import com.fsck.k9.mail.store.StorageManager;
+import com.fsck.k9.mail.store.local.LocalMessage;
 import com.fsck.k9.search.LocalSearch;
 import com.fsck.k9.search.SearchAccount;
 import com.fsck.k9.search.SearchSpecification;
@@ -1193,22 +1193,22 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
     }
 
     @Override
-    public void onResendMessage(Message message) {
+    public void onResendMessage(LocalMessage message) {
         MessageCompose.actionEditDraft(this, message.makeMessageReference());
     }
 
     @Override
-    public void onForward(Message message) {
+    public void onForward(LocalMessage message) {
         MessageCompose.actionForward(this, message, null);
     }
 
     @Override
-    public void onReply(Message message) {
+    public void onReply(LocalMessage message) {
         MessageCompose.actionReply(this, message, false, null);
     }
 
     @Override
-    public void onReplyAll(Message message) {
+    public void onReplyAll(LocalMessage message) {
         MessageCompose.actionReply(this, message, true, null);
     }
 
@@ -1399,17 +1399,17 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
     }
 
     @Override
-    public void onReply(Message message, PgpData pgpData) {
+    public void onReply(LocalMessage message, PgpData pgpData) {
         MessageCompose.actionReply(this, message, false, pgpData.getDecryptedData());
     }
 
     @Override
-    public void onReplyAll(Message message, PgpData pgpData) {
+    public void onReplyAll(LocalMessage message, PgpData pgpData) {
         MessageCompose.actionReply(this, message, true, pgpData.getDecryptedData());
     }
 
     @Override
-    public void onForward(Message mMessage, PgpData mPgpData) {
+    public void onForward(LocalMessage mMessage, PgpData mPgpData) {
         MessageCompose.actionForward(this, mMessage, mPgpData.getDecryptedData());
     }
 
diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java
index 458c713c7..d52c24183 100644
--- a/src/com/fsck/k9/controller/MessagingController.java
+++ b/src/com/fsck/k9/controller/MessagingController.java
@@ -214,7 +214,7 @@ public class MessagingController implements Runnable {
          * Don't modify this list directly, but use {@link addMessage} and
          * {@link removeMatchingMessage} instead.
          */
-        LinkedList messages;
+        LinkedList messages;
         /**
          * List of references for messages that the user is still to be notified of,
          * but which don't fit into the inbox style anymore. It's sorted from newest
@@ -238,7 +238,7 @@ public class MessagingController implements Runnable {
         public NotificationData(int unread) {
             unreadBeforeNotification = unread;
             droppedMessages = new LinkedList();
-            messages = new LinkedList();
+            messages = new LinkedList();
         }
 
         /**
@@ -249,9 +249,9 @@ public class MessagingController implements Runnable {
          *
          * @param m The new message to add.
          */
-        public void addMessage(Message m) {
+        public void addMessage(LocalMessage m) {
             while (messages.size() >= MAX_MESSAGES) {
-                Message dropped = messages.removeLast();
+                LocalMessage dropped = messages.removeLast();
                 droppedMessages.addFirst(dropped.makeMessageReference());
             }
             messages.addFirst(m);
@@ -272,10 +272,10 @@ public class MessagingController implements Runnable {
                 }
             }
 
-            for (Message message : messages) {
+            for (LocalMessage message : messages) {
                 if (message.makeMessageReference().equals(ref)) {
                     if (messages.remove(message) && !droppedMessages.isEmpty()) {
-                        Message restoredMessage = droppedMessages.getFirst().restoreToLocalMessage(context);
+                        LocalMessage restoredMessage = droppedMessages.getFirst().restoreToLocalMessage(context);
                         if (restoredMessage != null) {
                             messages.addLast(restoredMessage);
                             droppedMessages.removeFirst();
@@ -293,7 +293,7 @@ public class MessagingController implements Runnable {
          * List.
          */
         public void supplyAllMessageRefs(List refs) {
-            for (Message m : messages) {
+            for (LocalMessage m : messages) {
                 refs.add(m.makeMessageReference());
             }
             refs.addAll(droppedMessages);
@@ -1621,7 +1621,7 @@ public class MessagingController implements Runnable {
                     }
 
                     // Store the updated message locally
-                    final Message localMessage = localFolder.storeSmallMessage(message, new Runnable() {
+                    final LocalMessage localMessage = localFolder.storeSmallMessage(message, new Runnable() {
                         @Override
                         public void run() {
                             progress.incrementAndGet();
@@ -1769,7 +1769,7 @@ public class MessagingController implements Runnable {
             // Update the listener with what we've found
             progress.incrementAndGet();
             // TODO do we need to re-fetch this here?
-            Message localMessage = localFolder.getMessage(message.getUid());
+            LocalMessage localMessage = localFolder.getMessage(message.getUid());
 
             // Increment the number of "new messages" if the newly downloaded message is
             // not marked as read.
@@ -4781,8 +4781,7 @@ public class MessagingController implements Runnable {
         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
     }
 
-    private Message findNewestMessageForNotificationLocked(Context context,
-            Account account, NotificationData data) {
+    private LocalMessage findNewestMessageForNotificationLocked(Context context, NotificationData data) {
         if (!data.messages.isEmpty()) {
             return data.messages.getFirst();
         }
@@ -4798,7 +4797,7 @@ public class MessagingController implements Runnable {
      * Creates a notification of a newly received message.
      */
     private void notifyAccount(Context context, Account account,
-            Message message, int previousUnreadMessageCount) {
+            LocalMessage message, int previousUnreadMessageCount) {
         final NotificationData data = getNotificationData(account, previousUnreadMessageCount);
         synchronized (data) {
             notifyAccountWithDataLocked(context, account, message, data);
@@ -4809,12 +4808,12 @@ public class MessagingController implements Runnable {
     private static final int NUM_SENDERS_IN_LOCK_SCREEN_NOTIFICATION = 5;
 
     private void notifyAccountWithDataLocked(Context context, Account account,
-            Message message, NotificationData data) {
+            LocalMessage message, NotificationData data) {
         boolean updateSilently = false;
 
         if (message == null) {
             /* this can happen if a message we previously notified for is read or deleted remotely */
-            message = findNewestMessageForNotificationLocked(context, account, data);
+            message = findNewestMessageForNotificationLocked(context, data);
             updateSilently = true;
             if (message == null) {
                 // seemingly both the message list as well as the overflow list is empty;
@@ -5110,7 +5109,7 @@ public class MessagingController implements Runnable {
                                                  int unreadCount,
                                                  CharSequence accountDescription,
                                                  CharSequence formattedSender,
-                                                 List messages) {
+                                                 List messages) {
         if (!platformSupportsLockScreenNotifications()) {
             return;
         }
diff --git a/src/com/fsck/k9/fragment/MessageListFragment.java b/src/com/fsck/k9/fragment/MessageListFragment.java
index 48e5b6190..fa70da72c 100644
--- a/src/com/fsck/k9/fragment/MessageListFragment.java
+++ b/src/com/fsck/k9/fragment/MessageListFragment.java
@@ -1190,19 +1190,19 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
         }
     }
 
-    public void onReply(Message message) {
+    public void onReply(LocalMessage message) {
         mFragmentListener.onReply(message);
     }
 
-    public void onReplyAll(Message message) {
+    public void onReplyAll(LocalMessage message) {
         mFragmentListener.onReplyAll(message);
     }
 
-    public void onForward(Message message) {
+    public void onForward(LocalMessage message) {
         mFragmentListener.onForward(message);
     }
 
-    public void onResendMessage(Message message) {
+    public void onResendMessage(LocalMessage message) {
         mFragmentListener.onResendMessage(message);
     }
 
@@ -1510,23 +1510,19 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
                 break;
             }
             case R.id.reply: {
-                Message message = getMessageAtPosition(adapterPosition);
-                onReply(message);
+                onReply(getMessageAtPosition(adapterPosition));
                 break;
             }
             case R.id.reply_all: {
-                Message message = getMessageAtPosition(adapterPosition);
-                onReplyAll(message);
+                onReplyAll(getMessageAtPosition(adapterPosition));
                 break;
             }
             case R.id.forward: {
-                Message message = getMessageAtPosition(adapterPosition);
-                onForward(message);
+                onForward(getMessageAtPosition(adapterPosition));
                 break;
             }
             case R.id.send_again: {
-                Message message = getMessageAtPosition(adapterPosition);
-                onResendMessage(message);
+                onResendMessage(getMessageAtPosition(adapterPosition));
                 mSelectedCount = 0;
                 break;
             }
@@ -3113,10 +3109,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
         void setMessageListProgress(int level);
         void showThread(Account account, String folderName, long rootId);
         void showMoreFromSameSender(String senderAddress);
-        void onResendMessage(Message message);
-        void onForward(Message message);
-        void onReply(Message message);
-        void onReplyAll(Message message);
+        void onResendMessage(LocalMessage message);
+        void onForward(LocalMessage message);
+        void onReply(LocalMessage message);
+        void onReplyAll(LocalMessage message);
         void openMessage(MessageReference messageReference);
         void setMessageListTitle(String title);
         void setMessageListSubTitle(String subTitle);
diff --git a/src/com/fsck/k9/fragment/MessageViewFragment.java b/src/com/fsck/k9/fragment/MessageViewFragment.java
index c8cfea928..5945957e3 100644
--- a/src/com/fsck/k9/fragment/MessageViewFragment.java
+++ b/src/com/fsck/k9/fragment/MessageViewFragment.java
@@ -846,10 +846,10 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
     }
 
     public interface MessageViewFragmentListener {
-        public void onForward(Message mMessage, PgpData mPgpData);
+        public void onForward(LocalMessage mMessage, PgpData mPgpData);
         public void disableDeleteAction();
-        public void onReplyAll(Message mMessage, PgpData mPgpData);
-        public void onReply(Message mMessage, PgpData mPgpData);
+        public void onReplyAll(LocalMessage mMessage, PgpData mPgpData);
+        public void onReply(LocalMessage mMessage, PgpData mPgpData);
         public void displayMessageSubject(String title);
         public void setProgress(boolean b);
         public void showNextMessageOrReturn();
diff --git a/src/com/fsck/k9/mail/Message.java b/src/com/fsck/k9/mail/Message.java
index 452d312df..d97982a4d 100644
--- a/src/com/fsck/k9/mail/Message.java
+++ b/src/com/fsck/k9/mail/Message.java
@@ -2,26 +2,20 @@
 package com.fsck.k9.mail;
 
 import java.io.IOException;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.EnumSet;
-import java.util.HashSet;
 import java.util.Set;
 
 import android.util.Log;
 
 import com.fsck.k9.K9;
-import com.fsck.k9.activity.MessageReference;
 import com.fsck.k9.mail.filter.CountingOutputStream;
 import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
 import com.fsck.k9.mail.store.UnavailableStorageException;
 
 
 public abstract class Message implements Part, CompositeBody {
-    protected MessageReference mReference;
-
-
     public enum RecipientType {
         TO, CC, BCC,
     }
@@ -73,7 +67,6 @@ public abstract class Message implements Part, CompositeBody {
     }
 
     public void setUid(String uid) {
-        this.mReference = null;
         this.mUid = uid;
     }
 
@@ -251,15 +244,6 @@ public abstract class Message implements Part, CompositeBody {
 
     public abstract void setCharset(String charset) throws MessagingException;
 
-    public MessageReference makeMessageReference() {
-        if (mReference == null) {
-            mReference = new MessageReference();
-            mReference.folderName  = getFolder().getName();
-            mReference.uid = mUid;
-        }
-        return mReference;
-    }
-
     public long calculateSize() {
         try {
 
@@ -279,14 +263,12 @@ public abstract class Message implements Part, CompositeBody {
     /**
      * Copy the contents of this object into another {@code Message} object.
      *
-     * @param destination
-     *         The {@code Message} object to receive the contents of this instance.
+     * @param destination The {@code Message} object to receive the contents of this instance.
      */
     protected void copy(Message destination) {
         destination.mUid = mUid;
         destination.mInternalDate = mInternalDate;
         destination.mFolder = mFolder;
-        destination.mReference = mReference;
 
         // mFlags contents can change during the object lifetime, so copy the Set
         destination.mFlags = EnumSet.copyOf(mFlags);
diff --git a/src/com/fsck/k9/mail/internet/MimeMessage.java b/src/com/fsck/k9/mail/internet/MimeMessage.java
index 75fa217d8..0d7bb8bab 100644
--- a/src/com/fsck/k9/mail/internet/MimeMessage.java
+++ b/src/com/fsck/k9/mail/internet/MimeMessage.java
@@ -608,28 +608,27 @@ public class MimeMessage extends Message {
     /**
      * Copy the contents of this object into another {@code MimeMessage} object.
      *
-     * @param message
-     *         The {@code MimeMessage} object to receive the contents of this instance.
+     * @param destination The {@code MimeMessage} object to receive the contents of this instance.
      */
-    protected void copy(MimeMessage message) {
-        super.copy(message);
+    protected void copy(MimeMessage destination) {
+        super.copy(destination);
 
-        message.mHeader = mHeader.clone();
+        destination.mHeader = mHeader.clone();
 
-        message.mBody = mBody;
-        message.mMessageId = mMessageId;
-        message.mSentDate = mSentDate;
-        message.mDateFormat = mDateFormat;
-        message.mSize = mSize;
+        destination.mBody = mBody;
+        destination.mMessageId = mMessageId;
+        destination.mSentDate = mSentDate;
+        destination.mDateFormat = mDateFormat;
+        destination.mSize = mSize;
 
         // These arrays are not supposed to be modified, so it's okay to reuse the references
-        message.mFrom = mFrom;
-        message.mTo = mTo;
-        message.mCc = mCc;
-        message.mBcc = mBcc;
-        message.mReplyTo = mReplyTo;
-        message.mReferences = mReferences;
-        message.mInReplyTo = mInReplyTo;
+        destination.mFrom = mFrom;
+        destination.mTo = mTo;
+        destination.mCc = mCc;
+        destination.mBcc = mBcc;
+        destination.mReplyTo = mReplyTo;
+        destination.mReferences = mReferences;
+        destination.mInReplyTo = mInReplyTo;
     }
 
     @Override
diff --git a/src/com/fsck/k9/mail/store/local/LocalFolder.java b/src/com/fsck/k9/mail/store/local/LocalFolder.java
index d4273d03f..8ca5c139c 100644
--- a/src/com/fsck/k9/mail/store/local/LocalFolder.java
+++ b/src/com/fsck/k9/mail/store/local/LocalFolder.java
@@ -1138,14 +1138,14 @@ public class LocalFolder extends Folder implements Serializable {
      * @return The local version of the message. Never null.
      * @throws MessagingException
      */
-    public Message storeSmallMessage(final Message message, final Runnable runnable) throws MessagingException {
-        return this.localStore.database.execute(true, new DbCallback() {
+    public LocalMessage storeSmallMessage(final Message message, final Runnable runnable) throws MessagingException {
+        return this.localStore.database.execute(true, new DbCallback() {
             @Override
-            public Message doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
+            public LocalMessage doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
                 try {
                     appendMessages(Collections.singletonList(message));
                     final String uid = message.getUid();
-                    final Message result = getMessage(uid);
+                    final LocalMessage result = getMessage(uid);
                     runnable.run();
                     // Set a flag indicating this message has now be fully downloaded
                     result.setFlag(Flag.X_DOWNLOADED_FULL, true);
diff --git a/src/com/fsck/k9/mail/store/local/LocalMessage.java b/src/com/fsck/k9/mail/store/local/LocalMessage.java
index 6dd164f14..7391a8646 100644
--- a/src/com/fsck/k9/mail/store/local/LocalMessage.java
+++ b/src/com/fsck/k9/mail/store/local/LocalMessage.java
@@ -27,7 +27,7 @@ import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback;
 import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException;
 
 public class LocalMessage extends MimeMessage {
-
+    protected MessageReference mReference;
     private final LocalStore localStore;
 
     private long mId;
@@ -52,8 +52,7 @@ public class LocalMessage extends MimeMessage {
         this.mFolder = folder;
     }
 
-    void populateFromGetMessageCursor(Cursor cursor)
-    throws MessagingException {
+    void populateFromGetMessageCursor(Cursor cursor) throws MessagingException {
         final String subject = cursor.getString(0);
         this.setSubject(subject == null ? "" : subject);
 
@@ -191,6 +190,12 @@ public class LocalMessage extends MimeMessage {
         mMessageDirty = true;
     }
 
+    @Override
+    public void setUid(String uid) {
+        super.setUid(uid);
+        this.mReference = null;
+    }
+
     @Override
     public boolean hasAttachments() {
         return (mAttachmentCount > 0);
@@ -564,15 +569,24 @@ public class LocalMessage extends MimeMessage {
         return localStore.getAccount();
     }
 
-    @Override
     public MessageReference makeMessageReference() {
         if (mReference == null) {
-            mReference = super.makeMessageReference();
+            mReference = new MessageReference();
+            mReference.folderName  = getFolder().getName();
+            mReference.uid = mUid;
             mReference.accountUuid = getFolder().getUuid();
         }
         return mReference;
     }
 
+    @Override
+    protected void copy(MimeMessage destination) {
+        super.copy(destination);
+        if (destination instanceof LocalMessage) {
+            ((LocalMessage)destination).mReference = mReference;
+        }
+    }
+
     @Override
     public LocalFolder getFolder() {
         return (LocalFolder) super.getFolder();
diff --git a/src/com/fsck/k9/service/NotificationActionService.java b/src/com/fsck/k9/service/NotificationActionService.java
index 2baef7560..079222e69 100644
--- a/src/com/fsck/k9/service/NotificationActionService.java
+++ b/src/com/fsck/k9/service/NotificationActionService.java
@@ -103,8 +103,8 @@ public class NotificationActionService extends CoreService {
                 if (K9.DEBUG)
                     Log.i(K9.LOG_TAG, "NotificationActionService initiating reply");
 
-                MessageReference ref = (MessageReference) intent.getParcelableExtra(EXTRA_MESSAGE);
-                Message message = ref.restoreToLocalMessage(this);
+                MessageReference ref = intent.getParcelableExtra(EXTRA_MESSAGE);
+                LocalMessage message = ref.restoreToLocalMessage(this);
                 if (message != null) {
                     Intent i = MessageCompose.getActionReplyIntent(this, message, false, null);
                     i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

From 2e98ff56e5719692b15cf051e78f74ad2f22c3a9 Mon Sep 17 00:00:00 2001
From: Jan Berkel 
Date: Fri, 12 Dec 2014 13:35:36 +0000
Subject: [PATCH 08/29] Break dependencies

---
 src/com/fsck/k9/activity/MessageCompose.java    |  3 ++-
 .../fsck/k9/activity/setup/WelcomeMessage.java  |  2 +-
 src/com/fsck/k9/helper/Contacts.java            |  2 +-
 src/com/fsck/k9/mail/Address.java               | 17 ++++++++---------
 .../internet}/HtmlConverter.java                |  4 +++-
 .../internet}/InsertableHtmlContent.java        |  2 +-
 src/com/fsck/k9/mail/internet/MimeUtility.java  |  1 -
 .../fsck/k9/mail/internet/TextBodyBuilder.java  |  4 +---
 .../fsck/k9/mail/store/local/LocalFolder.java   |  2 +-
 src/com/fsck/k9/view/MessageWebView.java        |  2 +-
 src/com/fsck/k9/view/SingleMessageView.java     |  3 +--
 .../k9/mail/internet/TextBodyBuilderTest.java   |  2 +-
 .../com/fsck/k9/helper/HtmlConverterTest.java   |  2 ++
 13 files changed, 23 insertions(+), 23 deletions(-)
 rename src/com/fsck/k9/{helper => mail/internet}/HtmlConverter.java (99%)
 rename src/com/fsck/k9/{activity => mail/internet}/InsertableHtmlContent.java (99%)

diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java
index 9018cba8a..076ee4fa6 100644
--- a/src/com/fsck/k9/activity/MessageCompose.java
+++ b/src/com/fsck/k9/activity/MessageCompose.java
@@ -89,12 +89,13 @@ import com.fsck.k9.crypto.PgpData;
 import com.fsck.k9.fragment.ProgressDialogFragment;
 import com.fsck.k9.helper.ContactItem;
 import com.fsck.k9.helper.Contacts;
-import com.fsck.k9.helper.HtmlConverter;
+import com.fsck.k9.mail.internet.HtmlConverter;
 import com.fsck.k9.helper.IdentityHelper;
 import com.fsck.k9.helper.Utility;
 import com.fsck.k9.mail.Address;
 import com.fsck.k9.mail.Body;
 import com.fsck.k9.mail.Flag;
+import com.fsck.k9.mail.internet.InsertableHtmlContent;
 import com.fsck.k9.mail.Message;
 import com.fsck.k9.mail.Message.RecipientType;
 import com.fsck.k9.mail.MessagingException;
diff --git a/src/com/fsck/k9/activity/setup/WelcomeMessage.java b/src/com/fsck/k9/activity/setup/WelcomeMessage.java
index 9c30bb0c2..e40a95eb9 100644
--- a/src/com/fsck/k9/activity/setup/WelcomeMessage.java
+++ b/src/com/fsck/k9/activity/setup/WelcomeMessage.java
@@ -12,7 +12,7 @@ import android.widget.TextView;
 import com.fsck.k9.R;
 import com.fsck.k9.activity.Accounts;
 import com.fsck.k9.activity.K9Activity;
-import com.fsck.k9.helper.HtmlConverter;
+import com.fsck.k9.mail.internet.HtmlConverter;
 
 /**
  * Displays a welcome message when no accounts have been created yet.
diff --git a/src/com/fsck/k9/helper/Contacts.java b/src/com/fsck/k9/helper/Contacts.java
index 022139f89..f5d25a912 100644
--- a/src/com/fsck/k9/helper/Contacts.java
+++ b/src/com/fsck/k9/helper/Contacts.java
@@ -18,7 +18,7 @@ import java.util.List;
 /**
  * Helper class to access the contacts stored on the device.
  */
-public class Contacts {
+public class Contacts implements Address.Lookup {
     /**
      * The order in which the search results are returned by
      * {@link #searchContacts(CharSequence)}.
diff --git a/src/com/fsck/k9/mail/Address.java b/src/com/fsck/k9/mail/Address.java
index f3810f051..25929e94e 100644
--- a/src/com/fsck/k9/mail/Address.java
+++ b/src/com/fsck/k9/mail/Address.java
@@ -20,11 +20,14 @@ import android.text.util.Rfc822Tokenizer;
 import android.util.Log;
 
 import com.fsck.k9.K9;
-import com.fsck.k9.helper.Contacts;
 import com.fsck.k9.helper.Utility;
 
 
 public class Address {
+    public static interface Lookup {
+        String getNameForAddress(String address);
+    }
+
     /**
      * If the number of addresses exceeds this value the addresses aren't
      * resolved to the names of Android contacts.
@@ -34,7 +37,7 @@ public class Address {
      * performance tests.
      * 

* - * @see Address#toFriendly(Address[], Contacts) + * @see Address#toFriendly(Address[], com.fsck.k9.mail.Address.Lookup) */ private static final int TOO_MANY_ADDRESSES = 50; @@ -239,7 +242,7 @@ public class Address { * @return */ public CharSequence toFriendly() { - return toFriendly((Contacts)null); + return toFriendly((Lookup)null); } /** @@ -254,7 +257,7 @@ public class Address { * @return * A "friendly" name for this {@link Address}. */ - public CharSequence toFriendly(final Contacts contacts) { + public CharSequence toFriendly(final Lookup contacts) { if (!K9.showCorrespondentNames()) { return mAddress; @@ -281,11 +284,7 @@ public class Address { return (!TextUtils.isEmpty(mPersonal)) ? mPersonal : mAddress; } - public static CharSequence toFriendly(Address[] addresses) { - return toFriendly(addresses, null); - } - - public static CharSequence toFriendly(Address[] addresses, Contacts contacts) { + public static CharSequence toFriendly(Address[] addresses, Lookup contacts) { if (addresses == null) { return null; } diff --git a/src/com/fsck/k9/helper/HtmlConverter.java b/src/com/fsck/k9/mail/internet/HtmlConverter.java similarity index 99% rename from src/com/fsck/k9/helper/HtmlConverter.java rename to src/com/fsck/k9/mail/internet/HtmlConverter.java index 14eb75fda..a217d5b81 100644 --- a/src/com/fsck/k9/helper/HtmlConverter.java +++ b/src/com/fsck/k9/mail/internet/HtmlConverter.java @@ -1,9 +1,11 @@ -package com.fsck.k9.helper; +package com.fsck.k9.mail.internet; import android.text.*; import android.text.Html.TagHandler; import android.util.Log; import com.fsck.k9.K9; +import com.fsck.k9.helper.Regex; + import org.xml.sax.XMLReader; import java.io.IOException; diff --git a/src/com/fsck/k9/activity/InsertableHtmlContent.java b/src/com/fsck/k9/mail/internet/InsertableHtmlContent.java similarity index 99% rename from src/com/fsck/k9/activity/InsertableHtmlContent.java rename to src/com/fsck/k9/mail/internet/InsertableHtmlContent.java index 9821ad784..fabcc6082 100644 --- a/src/com/fsck/k9/activity/InsertableHtmlContent.java +++ b/src/com/fsck/k9/mail/internet/InsertableHtmlContent.java @@ -1,4 +1,4 @@ -package com.fsck.k9.activity; +package com.fsck.k9.mail.internet; import java.io.Serializable; diff --git a/src/com/fsck/k9/mail/internet/MimeUtility.java b/src/com/fsck/k9/mail/internet/MimeUtility.java index c6308a313..6aaa93256 100644 --- a/src/com/fsck/k9/mail/internet/MimeUtility.java +++ b/src/com/fsck/k9/mail/internet/MimeUtility.java @@ -5,7 +5,6 @@ import android.content.Context; import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.R; -import com.fsck.k9.helper.HtmlConverter; import com.fsck.k9.mail.*; import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.internet.BinaryTempFileBody.BinaryTempFileBodyInputStream; diff --git a/src/com/fsck/k9/mail/internet/TextBodyBuilder.java b/src/com/fsck/k9/mail/internet/TextBodyBuilder.java index 7efbc8f5d..962a80e14 100644 --- a/src/com/fsck/k9/mail/internet/TextBodyBuilder.java +++ b/src/com/fsck/k9/mail/internet/TextBodyBuilder.java @@ -4,8 +4,6 @@ import android.text.TextUtils; import android.util.Log; import com.fsck.k9.K9; -import com.fsck.k9.activity.InsertableHtmlContent; -import com.fsck.k9.helper.HtmlConverter; import com.fsck.k9.mail.Body; public class TextBodyBuilder { @@ -27,7 +25,7 @@ public class TextBodyBuilder { /** * Build the {@link Body} that will contain the text of the message. * - * @return {@link TextBody} instance that contains the entered text and + * @return {@link com.fsck.k9.mail.internet.TextBody} instance that contains the entered text and * possibly the quoted original message. */ public TextBody buildTextHtml() { diff --git a/src/com/fsck/k9/mail/store/local/LocalFolder.java b/src/com/fsck/k9/mail/store/local/LocalFolder.java index 8ca5c139c..e68d7d684 100644 --- a/src/com/fsck/k9/mail/store/local/LocalFolder.java +++ b/src/com/fsck/k9/mail/store/local/LocalFolder.java @@ -32,7 +32,7 @@ import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.Account.MessageFormat; import com.fsck.k9.activity.Search; -import com.fsck.k9.helper.HtmlConverter; +import com.fsck.k9.mail.internet.HtmlConverter; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Body; diff --git a/src/com/fsck/k9/view/MessageWebView.java b/src/com/fsck/k9/view/MessageWebView.java index 3198d4723..07faaf3e1 100644 --- a/src/com/fsck/k9/view/MessageWebView.java +++ b/src/com/fsck/k9/view/MessageWebView.java @@ -10,7 +10,7 @@ import android.widget.Toast; import com.fsck.k9.K9; import com.fsck.k9.R; -import com.fsck.k9.helper.HtmlConverter; +import com.fsck.k9.mail.internet.HtmlConverter; public class MessageWebView extends RigidWebView { diff --git a/src/com/fsck/k9/view/SingleMessageView.java b/src/com/fsck/k9/view/SingleMessageView.java index 1506bd4dd..a351e7921 100644 --- a/src/com/fsck/k9/view/SingleMessageView.java +++ b/src/com/fsck/k9/view/SingleMessageView.java @@ -5,7 +5,6 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; -import java.net.URLDecoder; import android.app.Activity; import android.app.Fragment; @@ -47,7 +46,7 @@ import com.fsck.k9.fragment.MessageViewFragment; import com.fsck.k9.helper.ClipboardManager; import com.fsck.k9.helper.Contacts; import com.fsck.k9.helper.FileHelper; -import com.fsck.k9.helper.HtmlConverter; +import com.fsck.k9.mail.internet.HtmlConverter; import com.fsck.k9.helper.UrlEncodingHelper; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Address; diff --git a/tests-on-jvm/src/com/fsck/k9/mail/internet/TextBodyBuilderTest.java b/tests-on-jvm/src/com/fsck/k9/mail/internet/TextBodyBuilderTest.java index 64882691d..7c3b096dd 100644 --- a/tests-on-jvm/src/com/fsck/k9/mail/internet/TextBodyBuilderTest.java +++ b/tests-on-jvm/src/com/fsck/k9/mail/internet/TextBodyBuilderTest.java @@ -7,7 +7,7 @@ import org.junit.experimental.theories.*; import org.junit.runner.RunWith; import com.fsck.k9.Account.QuoteStyle; -import com.fsck.k9.activity.InsertableHtmlContent; +import com.fsck.k9.activity.TextBodyBuilder; class TestingTextBodyBuilder extends TextBodyBuilder { diff --git a/tests/src/com/fsck/k9/helper/HtmlConverterTest.java b/tests/src/com/fsck/k9/helper/HtmlConverterTest.java index c79958150..7ab254baf 100644 --- a/tests/src/com/fsck/k9/helper/HtmlConverterTest.java +++ b/tests/src/com/fsck/k9/helper/HtmlConverterTest.java @@ -1,5 +1,7 @@ package com.fsck.k9.helper; +import com.fsck.k9.mail.internet.HtmlConverter; + import junit.framework.TestCase; import java.io.BufferedWriter; From 40041ac0e0930525f2fa1fae2d24852df5cad6e3 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Fri, 12 Dec 2014 15:02:59 +0000 Subject: [PATCH 09/29] Move local message code to local package + cut some helper dependencies --- src/com/fsck/k9/Account.java | 17 +-- src/com/fsck/k9/EmailAddressValidator.java | 14 ++- src/com/fsck/k9/K9.java | 4 +- src/com/fsck/k9/Preferences.java | 2 +- src/com/fsck/k9/activity/Accounts.java | 2 +- src/com/fsck/k9/activity/FolderList.java | 3 +- src/com/fsck/k9/activity/MessageCompose.java | 19 +-- .../fsck/k9/activity/MessageInfoHolder.java | 4 +- src/com/fsck/k9/activity/MessageList.java | 4 +- .../fsck/k9/activity/MessageReference.java | 20 ++-- .../fsck/k9/activity/UpgradeDatabases.java | 2 +- .../k9/activity/setup/AccountSettings.java | 4 +- .../k9/activity/setup/FolderSettings.java | 4 +- src/com/fsck/k9/cache/EmailProviderCache.java | 4 +- .../k9/controller/MessagingController.java | 15 ++- .../MessagingControllerPushReceiver.java | 4 +- .../fsck/k9/controller/MessagingListener.java | 2 +- .../UnavailableAccountException.java | 8 +- .../fsck/k9/fragment/MessageListFragment.java | 6 +- .../fsck/k9/fragment/MessageViewFragment.java | 2 +- src/com/fsck/k9/helper/MessageHelper.java | 3 +- src/com/fsck/k9/helper/Regex.java | 109 ------------------ src/com/fsck/k9/helper/Utility.java | 59 ---------- .../local/AttachmentMessageBodyUtil.java | 2 +- .../local/BinaryAttachmentBody.java | 2 +- .../store => }/local/LocalAttachmentBody.java | 2 +- .../local/LocalAttachmentBodyPart.java | 2 +- .../local/LocalAttachmentMessageBody.java | 2 +- .../{mail/store => }/local/LocalFolder.java | 17 ++- .../{mail/store => }/local/LocalMessage.java | 21 ++-- .../k9/{mail/store => }/local/LocalStore.java | 27 +++-- .../{mail/store => }/local/LocalTextBody.java | 2 +- .../store => }/local/LockableDatabase.java | 11 +- .../local/MessageRemovalListener.java | 2 +- .../{mail/store => local}/StorageManager.java | 2 +- .../local/StoreSchemaDefinition.java | 2 +- .../{mail/store => }/local/TempFileBody.java | 2 +- .../store => }/local/TempFileMessageBody.java | 2 +- .../k9/{mail/store => }/local/ThreadInfo.java | 2 +- .../UnavailableStorageException.java | 2 +- src/com/fsck/k9/mail/Address.java | 46 +++++++- src/com/fsck/k9/mail/Body.java | 4 +- src/com/fsck/k9/mail/Folder.java | 1 - src/com/fsck/k9/mail/Message.java | 26 +---- .../local => }/MessageRetrievalListener.java | 3 +- src/com/fsck/k9/mail/ServerSettings.java | 5 +- src/com/fsck/k9/mail/Transport.java | 10 +- src/com/fsck/k9/mail/filter/Base64.java | 16 +++ .../fsck/k9/mail/internet/HtmlConverter.java | 69 ++++++++++- src/com/fsck/k9/mail/internet/MimeHeader.java | 4 +- .../fsck/k9/mail/internet/MimeMessage.java | 13 +-- .../{security => mail/ssl}/LocalKeyStore.java | 2 +- .../fsck/k9/mail/ssl/TrustManagerFactory.java | 1 - src/com/fsck/k9/mail/store/ImapStore.java | 29 +++-- .../k9/mail/store/{imap => }/ImapUtility.java | 4 +- src/com/fsck/k9/mail/store/Pop3Store.java | 5 +- src/com/fsck/k9/mail/store/WebDavStore.java | 6 +- .../fsck/k9/mail/transport/SmtpTransport.java | 22 ++-- .../fsck/k9/preferences/AccountSettings.java | 2 +- .../fsck/k9/preferences/SettingsImporter.java | 6 +- src/com/fsck/k9/preferences/Storage.java | 10 +- .../fsck/k9/provider/AttachmentProvider.java | 6 +- src/com/fsck/k9/provider/EmailProvider.java | 20 +++- src/com/fsck/k9/provider/MessageProvider.java | 6 +- src/com/fsck/k9/search/SqlQueryBuilder.java | 4 +- .../k9/service/DatabaseUpgradeService.java | 2 +- .../k9/service/NotificationActionService.java | 3 +- .../fsck/k9/service/StorageGoneReceiver.java | 2 +- src/com/fsck/k9/service/StorageReceiver.java | 2 +- src/com/fsck/k9/view/AttachmentView.java | 2 +- src/com/fsck/k9/view/SingleMessageView.java | 4 +- .../Address.java => mail/AddressTest.java} | 10 +- .../Address_quoteAtoms.java} | 8 +- .../k9/mail/ssl/TrustManagerFactoryTest.java | 3 - .../store/{imap => }/ImapUtilityTest.java | 3 +- 75 files changed, 352 insertions(+), 420 deletions(-) rename src/com/fsck/k9/{mail/store => controller}/UnavailableAccountException.java (83%) delete mode 100644 src/com/fsck/k9/helper/Regex.java rename src/com/fsck/k9/{mail/store => }/local/AttachmentMessageBodyUtil.java (96%) rename src/com/fsck/k9/{mail/store => }/local/BinaryAttachmentBody.java (97%) rename src/com/fsck/k9/{mail/store => }/local/LocalAttachmentBody.java (96%) rename src/com/fsck/k9/{mail/store => }/local/LocalAttachmentBodyPart.java (95%) rename src/com/fsck/k9/{mail/store => }/local/LocalAttachmentMessageBody.java (97%) rename src/com/fsck/k9/{mail/store => }/local/LocalFolder.java (99%) rename src/com/fsck/k9/{mail/store => }/local/LocalMessage.java (96%) rename src/com/fsck/k9/{mail/store => }/local/LocalStore.java (98%) rename src/com/fsck/k9/{mail/store => }/local/LocalTextBody.java (92%) rename src/com/fsck/k9/{mail/store => }/local/LockableDatabase.java (97%) rename src/com/fsck/k9/{mail/store => }/local/MessageRemovalListener.java (77%) rename src/com/fsck/k9/{mail/store => local}/StorageManager.java (99%) rename src/com/fsck/k9/{mail/store => }/local/StoreSchemaDefinition.java (99%) rename src/com/fsck/k9/{mail/store => }/local/TempFileBody.java (94%) rename src/com/fsck/k9/{mail/store => }/local/TempFileMessageBody.java (96%) rename src/com/fsck/k9/{mail/store => }/local/ThreadInfo.java (92%) rename src/com/fsck/k9/{mail/store => local}/UnavailableStorageException.java (96%) rename src/com/fsck/k9/mail/{store/local => }/MessageRetrievalListener.java (85%) rename src/com/fsck/k9/{security => mail/ssl}/LocalKeyStore.java (99%) rename src/com/fsck/k9/mail/store/{imap => }/ImapUtility.java (98%) rename tests/src/com/fsck/k9/{helper/Address.java => mail/AddressTest.java} (74%) rename tests/src/com/fsck/k9/{helper/Utility_quoteAtoms.java => mail/Address_quoteAtoms.java} (87%) rename tests/src/com/fsck/k9/mail/store/{imap => }/ImapUtilityTest.java (99%) diff --git a/src/com/fsck/k9/Account.java b/src/com/fsck/k9/Account.java index ac61af945..46f5c5d62 100644 --- a/src/com/fsck/k9/Account.java +++ b/src/com/fsck/k9/Account.java @@ -29,11 +29,12 @@ import com.fsck.k9.mail.Address; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Store; import com.fsck.k9.mail.Folder.FolderClass; +import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.store.RemoteStore; import com.fsck.k9.mail.store.StoreConfig; -import com.fsck.k9.mail.store.StorageManager; -import com.fsck.k9.mail.store.StorageManager.StorageProvider; -import com.fsck.k9.mail.store.local.LocalStore; +import com.fsck.k9.local.StorageManager; +import com.fsck.k9.local.StorageManager.StorageProvider; +import com.fsck.k9.local.LocalStore; import com.fsck.k9.provider.EmailProvider; import com.fsck.k9.provider.EmailProvider.StatsColumns; import com.fsck.k9.search.ConditionsTreeNode; @@ -42,7 +43,7 @@ import com.fsck.k9.search.SqlQueryBuilder; import com.fsck.k9.search.SearchSpecification.Attribute; import com.fsck.k9.search.SearchSpecification.SearchCondition; import com.fsck.k9.search.SearchSpecification.Searchfield; -import com.fsck.k9.security.LocalKeyStore; +import com.fsck.k9.mail.ssl.LocalKeyStore; import com.fsck.k9.view.ColorChip; import com.larswerkman.colorpicker.ColorPicker; @@ -363,9 +364,9 @@ public class Account implements BaseAccount, StoreConfig { SharedPreferences prefs = preferences.getPreferences(); - mStoreUri = Utility.base64Decode(prefs.getString(mUuid + ".storeUri", null)); + mStoreUri = Base64.decode(prefs.getString(mUuid + ".storeUri", null)); mLocalStorageProviderId = prefs.getString(mUuid + ".localStorageProvider", StorageManager.getInstance(K9.app).getDefaultProviderId()); - mTransportUri = Utility.base64Decode(prefs.getString(mUuid + ".transportUri", null)); + mTransportUri = Base64.decode(prefs.getString(mUuid + ".transportUri", null)); mDescription = prefs.getString(mUuid + ".description", null); mAlwaysBcc = prefs.getString(mUuid + ".alwaysBcc", mAlwaysBcc); mAutomaticCheckIntervalMinutes = prefs.getInt(mUuid + ".automaticCheckIntervalMinutes", -1); @@ -691,9 +692,9 @@ public class Account implements BaseAccount, StoreConfig { editor.putString("accountUuids", accountUuids); } - editor.putString(mUuid + ".storeUri", Utility.base64Encode(mStoreUri)); + editor.putString(mUuid + ".storeUri", Base64.encode(mStoreUri)); editor.putString(mUuid + ".localStorageProvider", mLocalStorageProviderId); - editor.putString(mUuid + ".transportUri", Utility.base64Encode(mTransportUri)); + editor.putString(mUuid + ".transportUri", Base64.encode(mTransportUri)); editor.putString(mUuid + ".description", mDescription); editor.putString(mUuid + ".alwaysBcc", mAlwaysBcc); editor.putInt(mUuid + ".automaticCheckIntervalMinutes", mAutomaticCheckIntervalMinutes); diff --git a/src/com/fsck/k9/EmailAddressValidator.java b/src/com/fsck/k9/EmailAddressValidator.java index 957a1384b..211d99220 100644 --- a/src/com/fsck/k9/EmailAddressValidator.java +++ b/src/com/fsck/k9/EmailAddressValidator.java @@ -4,7 +4,19 @@ package com.fsck.k9; import android.text.util.Rfc822Tokenizer; import android.widget.AutoCompleteTextView.Validator; +import java.util.regex.Pattern; + public class EmailAddressValidator implements Validator { + private static final Pattern EMAIL_ADDRESS_PATTERN = Pattern.compile( + "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" + + "\\@" + + "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + + "(" + + "\\." + + "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + + ")+" + ); + public CharSequence fixText(CharSequence invalidText) { return ""; } @@ -14,6 +26,6 @@ public class EmailAddressValidator implements Validator { } public boolean isValidAddressOnly(CharSequence text) { - return com.fsck.k9.helper.Regex.EMAIL_ADDRESS_PATTERN.matcher(text).matches(); + return EMAIL_ADDRESS_PATTERN.matcher(text).matches(); } } diff --git a/src/com/fsck/k9/K9.java b/src/com/fsck/k9/K9.java index 08c49c5ed..7e062d5f9 100644 --- a/src/com/fsck/k9/K9.java +++ b/src/com/fsck/k9/K9.java @@ -36,9 +36,9 @@ import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.internet.BinaryTempFileBody; -import com.fsck.k9.mail.store.local.LocalStore; +import com.fsck.k9.local.LocalStore; import com.fsck.k9.provider.UnreadWidgetProvider; -import com.fsck.k9.security.LocalKeyStore; +import com.fsck.k9.mail.ssl.LocalKeyStore; import com.fsck.k9.service.BootReceiver; import com.fsck.k9.service.MailService; import com.fsck.k9.service.ShutdownReceiver; diff --git a/src/com/fsck/k9/Preferences.java b/src/com/fsck/k9/Preferences.java index 04754af97..a5c0cd4bc 100644 --- a/src/com/fsck/k9/Preferences.java +++ b/src/com/fsck/k9/Preferences.java @@ -13,7 +13,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.util.Log; -import com.fsck.k9.mail.store.local.LocalStore; +import com.fsck.k9.local.LocalStore; import com.fsck.k9.preferences.Editor; import com.fsck.k9.preferences.Storage; diff --git a/src/com/fsck/k9/activity/Accounts.java b/src/com/fsck/k9/activity/Accounts.java index 5b7a757ae..c2a0ec21b 100644 --- a/src/com/fsck/k9/activity/Accounts.java +++ b/src/com/fsck/k9/activity/Accounts.java @@ -80,7 +80,7 @@ import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.store.RemoteStore; -import com.fsck.k9.mail.store.StorageManager; +import com.fsck.k9.local.StorageManager; import com.fsck.k9.mail.store.WebDavStore; import com.fsck.k9.preferences.SettingsExporter; import com.fsck.k9.preferences.SettingsImportExportException; diff --git a/src/com/fsck/k9/activity/FolderList.java b/src/com/fsck/k9/activity/FolderList.java index 2316b364f..d1d7c17e4 100644 --- a/src/com/fsck/k9/activity/FolderList.java +++ b/src/com/fsck/k9/activity/FolderList.java @@ -55,8 +55,7 @@ import com.fsck.k9.helper.power.TracingPowerManager; import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; -import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.mail.store.local.LocalFolder; +import com.fsck.k9.local.LocalFolder; import com.fsck.k9.search.LocalSearch; import com.fsck.k9.search.SearchSpecification.Attribute; import com.fsck.k9.search.SearchSpecification.Searchfield; diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index 076ee4fa6..d9f489052 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -89,6 +89,7 @@ import com.fsck.k9.crypto.PgpData; import com.fsck.k9.fragment.ProgressDialogFragment; import com.fsck.k9.helper.ContactItem; import com.fsck.k9.helper.Contacts; +import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.internet.HtmlConverter; import com.fsck.k9.helper.IdentityHelper; import com.fsck.k9.helper.Utility; @@ -108,10 +109,10 @@ import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mail.internet.TextBodyBuilder; -import com.fsck.k9.mail.store.local.LocalAttachmentBody; -import com.fsck.k9.mail.store.local.LocalMessage; -import com.fsck.k9.mail.store.local.TempFileBody; -import com.fsck.k9.mail.store.local.TempFileMessageBody; +import com.fsck.k9.local.LocalAttachmentBody; +import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.local.TempFileBody; +import com.fsck.k9.local.TempFileMessageBody; import com.fsck.k9.view.MessageWebView; import org.apache.james.mime4j.codec.EncoderUtil; @@ -1655,7 +1656,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, // First item is the body length. We use this to separate the composed reply from the quoted text. if (tokenizer.hasMoreTokens()) { - String bodyLengthS = Utility.base64Decode(tokenizer.nextToken()); + String bodyLengthS = Base64.decode(tokenizer.nextToken()); try { identity.put(IdentityField.LENGTH, Integer.valueOf(bodyLengthS).toString()); } catch (Exception e) { @@ -1663,16 +1664,16 @@ public class MessageCompose extends K9Activity implements OnClickListener, } } if (tokenizer.hasMoreTokens()) { - identity.put(IdentityField.SIGNATURE, Utility.base64Decode(tokenizer.nextToken())); + identity.put(IdentityField.SIGNATURE, Base64.decode(tokenizer.nextToken())); } if (tokenizer.hasMoreTokens()) { - identity.put(IdentityField.NAME, Utility.base64Decode(tokenizer.nextToken())); + identity.put(IdentityField.NAME, Base64.decode(tokenizer.nextToken())); } if (tokenizer.hasMoreTokens()) { - identity.put(IdentityField.EMAIL, Utility.base64Decode(tokenizer.nextToken())); + identity.put(IdentityField.EMAIL, Base64.decode(tokenizer.nextToken())); } if (tokenizer.hasMoreTokens()) { - identity.put(IdentityField.QUOTED_TEXT_MODE, Utility.base64Decode(tokenizer.nextToken())); + identity.put(IdentityField.QUOTED_TEXT_MODE, Base64.decode(tokenizer.nextToken())); } } diff --git a/src/com/fsck/k9/activity/MessageInfoHolder.java b/src/com/fsck/k9/activity/MessageInfoHolder.java index e217a0f78..379543525 100644 --- a/src/com/fsck/k9/activity/MessageInfoHolder.java +++ b/src/com/fsck/k9/activity/MessageInfoHolder.java @@ -1,8 +1,8 @@ package com.fsck.k9.activity; import java.util.Date; -import com.fsck.k9.mail.Message; -import com.fsck.k9.mail.store.local.LocalMessage; + +import com.fsck.k9.local.LocalMessage; public class MessageInfoHolder { public String date; diff --git a/src/com/fsck/k9/activity/MessageList.java b/src/com/fsck/k9/activity/MessageList.java index 604c1a166..ebfb3ef3b 100644 --- a/src/com/fsck/k9/activity/MessageList.java +++ b/src/com/fsck/k9/activity/MessageList.java @@ -42,8 +42,8 @@ import com.fsck.k9.fragment.MessageListFragment; import com.fsck.k9.fragment.MessageListFragment.MessageListFragmentListener; import com.fsck.k9.fragment.MessageViewFragment; import com.fsck.k9.fragment.MessageViewFragment.MessageViewFragmentListener; -import com.fsck.k9.mail.store.StorageManager; -import com.fsck.k9.mail.store.local.LocalMessage; +import com.fsck.k9.local.StorageManager; +import com.fsck.k9.local.LocalMessage; import com.fsck.k9.search.LocalSearch; import com.fsck.k9.search.SearchAccount; import com.fsck.k9.search.SearchSpecification; diff --git a/src/com/fsck/k9/activity/MessageReference.java b/src/com/fsck/k9/activity/MessageReference.java index bb2d6264d..0ba73a0c2 100644 --- a/src/com/fsck/k9/activity/MessageReference.java +++ b/src/com/fsck/k9/activity/MessageReference.java @@ -8,13 +8,11 @@ import android.util.Log; import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.Preferences; -import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Flag; -import com.fsck.k9.mail.Folder; -import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.mail.store.local.LocalFolder; -import com.fsck.k9.mail.store.local.LocalMessage; +import com.fsck.k9.local.LocalFolder; +import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.mail.filter.Base64; import java.util.StringTokenizer; @@ -51,9 +49,9 @@ public class MessageReference implements Parcelable { // Split the identity, stripping away the first two characters representing the version and delimiter. StringTokenizer tokens = new StringTokenizer(identity.substring(2), IDENTITY_SEPARATOR, false); if (tokens.countTokens() >= 3) { - accountUuid = Utility.base64Decode(tokens.nextToken()); - folderName = Utility.base64Decode(tokens.nextToken()); - uid = Utility.base64Decode(tokens.nextToken()); + accountUuid = Base64.decode(tokens.nextToken()); + folderName = Base64.decode(tokens.nextToken()); + uid = Base64.decode(tokens.nextToken()); if (tokens.hasMoreTokens()) { final String flagString = tokens.nextToken(); @@ -82,11 +80,11 @@ public class MessageReference implements Parcelable { refString.append(IDENTITY_VERSION_1); refString.append(IDENTITY_SEPARATOR); - refString.append(Utility.base64Encode(accountUuid)); + refString.append(Base64.encode(accountUuid)); refString.append(IDENTITY_SEPARATOR); - refString.append(Utility.base64Encode(folderName)); + refString.append(Base64.encode(folderName)); refString.append(IDENTITY_SEPARATOR); - refString.append(Utility.base64Encode(uid)); + refString.append(Base64.encode(uid)); if (flag != null) { refString.append(IDENTITY_SEPARATOR); refString.append(flag.name()); diff --git a/src/com/fsck/k9/activity/UpgradeDatabases.java b/src/com/fsck/k9/activity/UpgradeDatabases.java index b0cf3587a..f5b2b9754 100644 --- a/src/com/fsck/k9/activity/UpgradeDatabases.java +++ b/src/com/fsck/k9/activity/UpgradeDatabases.java @@ -30,7 +30,7 @@ import android.widget.TextView; *
  • {@link #actionUpgradeDatabases(Context, Intent)} will call {@link K9#areDatabasesUpToDate()} * to check if we already know whether the databases have been upgraded.
  • *
  • {@link K9#areDatabasesUpToDate()} will compare the last known database version stored in a - * {@link SharedPreferences} file to {@link com.fsck.k9.mail.store.local.LocalStore#DB_VERSION}. This + * {@link SharedPreferences} file to {@link com.fsck.k9.local.LocalStore#DB_VERSION}. This * is done as an optimization because it's faster than opening all of the accounts' databases * one by one.
  • *
  • If there was an error reading the cached database version or if it shows the databases need diff --git a/src/com/fsck/k9/activity/setup/AccountSettings.java b/src/com/fsck/k9/activity/setup/AccountSettings.java index ba4a82658..2eac23514 100644 --- a/src/com/fsck/k9/activity/setup/AccountSettings.java +++ b/src/com/fsck/k9/activity/setup/AccountSettings.java @@ -36,8 +36,8 @@ import com.fsck.k9.activity.K9PreferenceActivity; import com.fsck.k9.activity.ManageIdentities; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Store; -import com.fsck.k9.mail.store.local.LocalFolder; -import com.fsck.k9.mail.store.StorageManager; +import com.fsck.k9.local.LocalFolder; +import com.fsck.k9.local.StorageManager; import com.fsck.k9.service.MailService; import org.openintents.openpgp.util.OpenPgpListPreference; diff --git a/src/com/fsck/k9/activity/setup/FolderSettings.java b/src/com/fsck/k9/activity/setup/FolderSettings.java index 31bb8e95c..af9ab8b67 100644 --- a/src/com/fsck/k9/activity/setup/FolderSettings.java +++ b/src/com/fsck/k9/activity/setup/FolderSettings.java @@ -16,8 +16,8 @@ import com.fsck.k9.mail.Folder.FolderClass; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Store; -import com.fsck.k9.mail.store.local.LocalFolder; -import com.fsck.k9.mail.store.local.LocalStore; +import com.fsck.k9.local.LocalFolder; +import com.fsck.k9.local.LocalStore; import com.fsck.k9.service.MailService; public class FolderSettings extends K9PreferenceActivity { diff --git a/src/com/fsck/k9/cache/EmailProviderCache.java b/src/com/fsck/k9/cache/EmailProviderCache.java index ccd712d12..a37f66acd 100644 --- a/src/com/fsck/k9/cache/EmailProviderCache.java +++ b/src/com/fsck/k9/cache/EmailProviderCache.java @@ -11,8 +11,8 @@ import android.support.v4.content.LocalBroadcastManager; import com.fsck.k9.fragment.MessageListFragment; import com.fsck.k9.mail.Message; -import com.fsck.k9.mail.store.local.LocalFolder; -import com.fsck.k9.mail.store.local.LocalMessage; +import com.fsck.k9.local.LocalFolder; +import com.fsck.k9.local.LocalMessage; import com.fsck.k9.provider.EmailProvider; /** diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index d52c24183..a955c9199 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -81,15 +81,14 @@ import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; -import com.fsck.k9.mail.store.local.MessageRemovalListener; -import com.fsck.k9.mail.store.local.MessageRetrievalListener; -import com.fsck.k9.mail.store.local.LocalFolder; -import com.fsck.k9.mail.store.local.LocalMessage; -import com.fsck.k9.mail.store.local.LocalStore; -import com.fsck.k9.mail.store.local.LocalStore.PendingCommand; +import com.fsck.k9.local.MessageRemovalListener; +import com.fsck.k9.mail.MessageRetrievalListener; +import com.fsck.k9.local.LocalFolder; +import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.local.LocalStore; +import com.fsck.k9.local.LocalStore.PendingCommand; import com.fsck.k9.mail.store.Pop3Store; -import com.fsck.k9.mail.store.UnavailableAccountException; -import com.fsck.k9.mail.store.UnavailableStorageException; +import com.fsck.k9.local.UnavailableStorageException; import com.fsck.k9.provider.EmailProvider; import com.fsck.k9.provider.EmailProvider.StatsColumns; import com.fsck.k9.search.ConditionsTreeNode; diff --git a/src/com/fsck/k9/controller/MessagingControllerPushReceiver.java b/src/com/fsck/k9/controller/MessagingControllerPushReceiver.java index ccd13a59f..d99f5feb1 100644 --- a/src/com/fsck/k9/controller/MessagingControllerPushReceiver.java +++ b/src/com/fsck/k9/controller/MessagingControllerPushReceiver.java @@ -11,8 +11,8 @@ import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.PushReceiver; -import com.fsck.k9.mail.store.local.LocalFolder; -import com.fsck.k9.mail.store.local.LocalStore; +import com.fsck.k9.local.LocalFolder; +import com.fsck.k9.local.LocalStore; import com.fsck.k9.service.SleepService; import java.util.List; diff --git a/src/com/fsck/k9/controller/MessagingListener.java b/src/com/fsck/k9/controller/MessagingListener.java index 470cc6387..a0396b535 100644 --- a/src/com/fsck/k9/controller/MessagingListener.java +++ b/src/com/fsck/k9/controller/MessagingListener.java @@ -11,7 +11,7 @@ import com.fsck.k9.BaseAccount; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Part; -import com.fsck.k9.mail.store.local.LocalMessage; +import com.fsck.k9.local.LocalMessage; /** * Defines the interface that {@link MessagingController} will use to callback to requesters. diff --git a/src/com/fsck/k9/mail/store/UnavailableAccountException.java b/src/com/fsck/k9/controller/UnavailableAccountException.java similarity index 83% rename from src/com/fsck/k9/mail/store/UnavailableAccountException.java rename to src/com/fsck/k9/controller/UnavailableAccountException.java index 26e66b1be..55533828e 100644 --- a/src/com/fsck/k9/mail/store/UnavailableAccountException.java +++ b/src/com/fsck/k9/controller/UnavailableAccountException.java @@ -1,10 +1,8 @@ -package com.fsck.k9.mail.store; - -import com.fsck.k9.Account; +package com.fsck.k9.controller; /** - * An {@link Account} is not - * {@link Account#isAvailable(android.content.Context)}.
    + * An {@link com.fsck.k9.Account} is not + * {@link com.fsck.k9.Account#isAvailable(android.content.Context)}.
    * The operation may be retried later. */ public class UnavailableAccountException extends RuntimeException { diff --git a/src/com/fsck/k9/fragment/MessageListFragment.java b/src/com/fsck/k9/fragment/MessageListFragment.java index fa70da72c..dae7ec49d 100644 --- a/src/com/fsck/k9/fragment/MessageListFragment.java +++ b/src/com/fsck/k9/fragment/MessageListFragment.java @@ -88,9 +88,9 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.mail.store.local.LocalFolder; -import com.fsck.k9.mail.store.local.LocalMessage; -import com.fsck.k9.mail.store.local.LocalStore; +import com.fsck.k9.local.LocalFolder; +import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.local.LocalStore; import com.fsck.k9.provider.EmailProvider; import com.fsck.k9.provider.EmailProvider.MessageColumns; import com.fsck.k9.provider.EmailProvider.SpecialColumns; diff --git a/src/com/fsck/k9/fragment/MessageViewFragment.java b/src/com/fsck/k9/fragment/MessageViewFragment.java index 5945957e3..5ffee961a 100644 --- a/src/com/fsck/k9/fragment/MessageViewFragment.java +++ b/src/com/fsck/k9/fragment/MessageViewFragment.java @@ -38,7 +38,7 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; -import com.fsck.k9.mail.store.local.LocalMessage; +import com.fsck.k9.local.LocalMessage; import com.fsck.k9.view.AttachmentView; import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback; import com.fsck.k9.view.MessageHeader; diff --git a/src/com/fsck/k9/helper/MessageHelper.java b/src/com/fsck/k9/helper/MessageHelper.java index 5df0161b8..4cde05621 100644 --- a/src/com/fsck/k9/helper/MessageHelper.java +++ b/src/com/fsck/k9/helper/MessageHelper.java @@ -11,10 +11,9 @@ import com.fsck.k9.activity.FolderInfoHolder; import com.fsck.k9.activity.MessageInfoHolder; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Flag; -import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Message.RecipientType; -import com.fsck.k9.mail.store.local.LocalMessage; +import com.fsck.k9.local.LocalMessage; public class MessageHelper { diff --git a/src/com/fsck/k9/helper/Regex.java b/src/com/fsck/k9/helper/Regex.java deleted file mode 100644 index dd60134a0..000000000 --- a/src/com/fsck/k9/helper/Regex.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2007 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. - * - * Imported from AOSP on 2011-01-12 by JRV. - * Domain patterns updated from IANA on 2010-01-12 - * - * - */ - -package com.fsck.k9.helper; - -import java.util.regex.Pattern; - -/** - * Commonly used regular expression patterns. - */ -public class Regex { - - /** - * Goegular expression to match all IANA top-level domains for WEB_URL. - * List accurate as of 2011/01/12. List taken from: - * http://data.iana.org/TLD/tlds-alpha-by-domain.txt - * This pattern is auto-generated by frameworks/base/common/tools/make-iana-tld-pattern.py - */ - public static final String TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL = - "(?:" - + "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])" - + "|(?:biz|b[abdefghijmnorstvwyz])" - + "|(?:cat|com|coop|c[acdfghiklmnoruvxyz])" - + "|d[ejkmoz]" - + "|(?:edu|e[cegrstu])" - + "|f[ijkmor]" - + "|(?:gov|g[abdefghilmnpqrstuwy])" - + "|h[kmnrtu]" - + "|(?:info|int|i[delmnoqrst])" - + "|(?:jobs|j[emop])" - + "|k[eghimnprwyz]" - + "|l[abcikrstuvy]" - + "|(?:mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])" - + "|(?:name|net|n[acefgilopruz])" - + "|(?:org|om)" - + "|(?:pro|p[aefghklmnrstwy])" - + "|qa" - + "|r[eosuw]" - + "|s[abcdeghijklmnortuvyz]" - + "|(?:tel|travel|t[cdfghjklmnoprtvwz])" - + "|u[agksyz]" - + "|v[aceginu]" - + "|w[fs]" - + "|(?:xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s|xn\\-\\-fzc2c9e2c|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-j6w193g|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-kprw13d|xn\\-\\-kpry57d|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbayh7gpa|xn\\-\\-mgberp4a5d4ar|xn\\-\\-o3cw4h|xn\\-\\-p1ai|xn\\-\\-pgbs0dh|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a|xn\\-\\-xkc2al3hye2a|xn\\-\\-ygbi2ammx|xn\\-\\-zckzah)" - + "|y[et]" - + "|z[amw]))"; - - /* This comprises most common used Unicode characters allowed in IRI - * as detailed in RFC 3987. - * Specifically, those two byte Unicode characters are not included. - */ - public static final String GOOD_IRI_CHAR = - "a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"; - - /** - * Regular expression pattern to match most part of RFC 3987 - * Internationalized URLs, aka IRIs. Commonly used Unicode characters are - * added. - */ - public static final Pattern WEB_URL_PATTERN = Pattern.compile( - "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)" - + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_" - + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?" - + "((?:(?:[" + GOOD_IRI_CHAR + "][" + GOOD_IRI_CHAR + "\\-]{0,64}\\.)+" // named host - + TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL - + "|(?:(?:25[0-5]|2[0-4]" // or ip address - + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]" - + "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]" - + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}" - + "|[1-9][0-9]|[0-9])))" - + "(?:\\:\\d{1,5})?)" // plus option port number - + "(\\/(?:(?:[" + GOOD_IRI_CHAR + "\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus option query params - + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?" - + "(?:\\b|$)"); // and finally, a word boundary or end of - // input. This is to stop foo.sure from - // matching as foo.su - - public static final Pattern EMAIL_ADDRESS_PATTERN - = Pattern.compile( - "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" + - "\\@" + - "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + - "(" + - "\\." + - "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + - ")+" - ); - - public static final String BITCOIN_URI_PATTERN = - "bitcoin:[1-9a-km-zA-HJ-NP-Z]{27,34}(\\?[a-zA-Z0-9$\\-_.+!*'(),%:@&=]*)?"; -} diff --git a/src/com/fsck/k9/helper/Utility.java b/src/com/fsck/k9/helper/Utility.java index 50ca99e3a..ffd6270a2 100644 --- a/src/com/fsck/k9/helper/Utility.java +++ b/src/com/fsck/k9/helper/Utility.java @@ -15,7 +15,6 @@ import android.widget.EditText; import android.widget.TextView; import com.fsck.k9.K9; -import com.fsck.k9.mail.filter.Base64; import java.nio.charset.Charset; import java.util.ArrayList; @@ -91,22 +90,6 @@ public class Utility { return TextUtils.join(String.valueOf(separator), parts); } - public static String base64Decode(String encoded) { - if (encoded == null) { - return null; - } - byte[] decoded = new Base64().decode(encoded.getBytes()); - return new String(decoded); - } - - public static String base64Encode(String s) { - if (s == null) { - return s; - } - byte[] encoded = new Base64().encode(s.getBytes()); - return new String(encoded); - } - public static boolean requiredFieldValid(TextView view) { return view.getText() != null && view.getText().length() > 0; } @@ -130,48 +113,6 @@ public class Utility { return false; } - private static final Pattern ATOM = Pattern.compile("^(?:[a-zA-Z0-9!#$%&'*+\\-/=?^_`{|}~]|\\s)+$"); - - /** - * Quote a string, if necessary, based upon the definition of an "atom," as defined by RFC2822 - * (http://tools.ietf.org/html/rfc2822#section-3.2.4). Strings that consist purely of atoms are - * left unquoted; anything else is returned as a quoted string. - * @param text String to quote. - * @return Possibly quoted string. - */ - public static String quoteAtoms(final String text) { - if (ATOM.matcher(text).matches()) { - return text; - } else { - return quoteString(text); - } - } - - /** - * Ensures that the given string starts and ends with the double quote character. The string is not modified in any way except to add the - * double quote character to start and end if it's not already there. - * sample -> "sample" - * "sample" -> "sample" - * ""sample"" -> "sample" - * "sample"" -> "sample" - * sa"mp"le -> "sa"mp"le" - * "sa"mp"le" -> "sa"mp"le" - * (empty string) -> "" - * " -> "" - * @param s - * @return - */ - public static String quoteString(String s) { - if (s == null) { - return null; - } - if (!s.matches("^\".*\"$")) { - return "\"" + s + "\""; - } else { - return s; - } - } - /** * A fast version of URLDecoder.decode() that works only with UTF-8 and does only two * allocations. This version is around 3x as fast as the standard one and I'm using it diff --git a/src/com/fsck/k9/mail/store/local/AttachmentMessageBodyUtil.java b/src/com/fsck/k9/local/AttachmentMessageBodyUtil.java similarity index 96% rename from src/com/fsck/k9/mail/store/local/AttachmentMessageBodyUtil.java rename to src/com/fsck/k9/local/AttachmentMessageBodyUtil.java index a1f4d5b3b..97a8a2394 100644 --- a/src/com/fsck/k9/mail/store/local/AttachmentMessageBodyUtil.java +++ b/src/com/fsck/k9/local/AttachmentMessageBodyUtil.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import java.io.IOException; import java.io.InputStream; diff --git a/src/com/fsck/k9/mail/store/local/BinaryAttachmentBody.java b/src/com/fsck/k9/local/BinaryAttachmentBody.java similarity index 97% rename from src/com/fsck/k9/mail/store/local/BinaryAttachmentBody.java rename to src/com/fsck/k9/local/BinaryAttachmentBody.java index 8efcf8992..466f6e57f 100644 --- a/src/com/fsck/k9/mail/store/local/BinaryAttachmentBody.java +++ b/src/com/fsck/k9/local/BinaryAttachmentBody.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import java.io.IOException; import java.io.InputStream; diff --git a/src/com/fsck/k9/mail/store/local/LocalAttachmentBody.java b/src/com/fsck/k9/local/LocalAttachmentBody.java similarity index 96% rename from src/com/fsck/k9/mail/store/local/LocalAttachmentBody.java rename to src/com/fsck/k9/local/LocalAttachmentBody.java index 7efdf949c..acfbe7980 100644 --- a/src/com/fsck/k9/mail/store/local/LocalAttachmentBody.java +++ b/src/com/fsck/k9/local/LocalAttachmentBody.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; diff --git a/src/com/fsck/k9/mail/store/local/LocalAttachmentBodyPart.java b/src/com/fsck/k9/local/LocalAttachmentBodyPart.java similarity index 95% rename from src/com/fsck/k9/mail/store/local/LocalAttachmentBodyPart.java rename to src/com/fsck/k9/local/LocalAttachmentBodyPart.java index e72d3b493..7d5a19798 100644 --- a/src/com/fsck/k9/mail/store/local/LocalAttachmentBodyPart.java +++ b/src/com/fsck/k9/local/LocalAttachmentBodyPart.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import com.fsck.k9.mail.Body; import com.fsck.k9.mail.MessagingException; diff --git a/src/com/fsck/k9/mail/store/local/LocalAttachmentMessageBody.java b/src/com/fsck/k9/local/LocalAttachmentMessageBody.java similarity index 97% rename from src/com/fsck/k9/mail/store/local/LocalAttachmentMessageBody.java rename to src/com/fsck/k9/local/LocalAttachmentMessageBody.java index d953bfc73..6dc632495 100644 --- a/src/com/fsck/k9/mail/store/local/LocalAttachmentMessageBody.java +++ b/src/com/fsck/k9/local/LocalAttachmentMessageBody.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import java.io.IOException; import java.io.OutputStream; diff --git a/src/com/fsck/k9/mail/store/local/LocalFolder.java b/src/com/fsck/k9/local/LocalFolder.java similarity index 99% rename from src/com/fsck/k9/mail/store/local/LocalFolder.java rename to src/com/fsck/k9/local/LocalFolder.java index e68d7d684..33e177f9a 100644 --- a/src/com/fsck/k9/mail/store/local/LocalFolder.java +++ b/src/com/fsck/k9/local/LocalFolder.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import java.io.File; import java.io.FileOutputStream; @@ -32,6 +32,7 @@ import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.Account.MessageFormat; import com.fsck.k9.activity.Search; +import com.fsck.k9.mail.MessageRetrievalListener; import com.fsck.k9.mail.internet.HtmlConverter; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Address; @@ -51,10 +52,8 @@ import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mail.internet.MimeUtility.ViewableContainer; -import com.fsck.k9.mail.store.StorageManager; -import com.fsck.k9.mail.store.UnavailableStorageException; -import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback; -import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException; +import com.fsck.k9.local.LockableDatabase.DbCallback; +import com.fsck.k9.local.LockableDatabase.WrappedException; import com.fsck.k9.provider.AttachmentProvider; public class LocalFolder extends Folder implements Serializable { @@ -828,10 +827,10 @@ public class LocalFolder extends Folder implements Serializable { * The messages whose headers should be loaded. * @throws UnavailableStorageException */ - void populateHeaders(final List messages) throws UnavailableStorageException { + void populateHeaders(final List messages) throws MessagingException { this.localStore.database.execute(false, new DbCallback() { @Override - public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { + public Void doDbWork(final SQLiteDatabase db) throws WrappedException, MessagingException { Cursor cursor = null; if (messages.isEmpty()) { return null; @@ -1488,7 +1487,7 @@ public class LocalFolder extends Folder implements Serializable { private void saveHeaders(final long id, final MimeMessage message) throws MessagingException { this.localStore.database.execute(true, new DbCallback() { @Override - public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { + public Void doDbWork(final SQLiteDatabase db) throws WrappedException, MessagingException { deleteHeaders(id); for (String name : message.getHeaderNames()) { @@ -1517,7 +1516,7 @@ public class LocalFolder extends Folder implements Serializable { }); } - void deleteHeaders(final long id) throws UnavailableStorageException { + void deleteHeaders(final long id) throws MessagingException { this.localStore.database.execute(false, new DbCallback() { @Override public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { diff --git a/src/com/fsck/k9/mail/store/local/LocalMessage.java b/src/com/fsck/k9/local/LocalMessage.java similarity index 96% rename from src/com/fsck/k9/mail/store/local/LocalMessage.java rename to src/com/fsck/k9/local/LocalMessage.java index 7391a8646..ac3aa5918 100644 --- a/src/com/fsck/k9/mail/store/local/LocalMessage.java +++ b/src/com/fsck/k9/local/LocalMessage.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import java.io.IOException; import java.io.OutputStream; @@ -22,9 +22,8 @@ import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeUtility; -import com.fsck.k9.mail.store.UnavailableStorageException; -import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback; -import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException; +import com.fsck.k9.local.LockableDatabase.DbCallback; +import com.fsck.k9.local.LockableDatabase.WrappedException; public class LocalMessage extends MimeMessage { protected MessageReference mReference; @@ -499,44 +498,44 @@ public class LocalMessage extends MimeMessage { db.delete("threads", "message_id = ?", idArg); } - private void loadHeaders() throws UnavailableStorageException { + private void loadHeaders() throws MessagingException { List messages = new ArrayList(); messages.add(this); mHeadersLoaded = true; // set true before calling populate headers to stop recursion - ((LocalFolder) mFolder).populateHeaders(messages); + getFolder().populateHeaders(messages); } @Override - public void addHeader(String name, String value) throws UnavailableStorageException { + public void addHeader(String name, String value) throws MessagingException { if (!mHeadersLoaded) loadHeaders(); super.addHeader(name, value); } @Override - public void setHeader(String name, String value) throws UnavailableStorageException { + public void setHeader(String name, String value) throws MessagingException { if (!mHeadersLoaded) loadHeaders(); super.setHeader(name, value); } @Override - public String[] getHeader(String name) throws UnavailableStorageException { + public String[] getHeader(String name) throws MessagingException { if (!mHeadersLoaded) loadHeaders(); return super.getHeader(name); } @Override - public void removeHeader(String name) throws UnavailableStorageException { + public void removeHeader(String name) throws MessagingException { if (!mHeadersLoaded) loadHeaders(); super.removeHeader(name); } @Override - public Set getHeaderNames() throws UnavailableStorageException { + public Set getHeaderNames() throws MessagingException { if (!mHeadersLoaded) loadHeaders(); return super.getHeaderNames(); diff --git a/src/com/fsck/k9/mail/store/local/LocalStore.java b/src/com/fsck/k9/local/LocalStore.java similarity index 98% rename from src/com/fsck/k9/mail/store/local/LocalStore.java rename to src/com/fsck/k9/local/LocalStore.java index 64bacb2ef..a7ec36f51 100644 --- a/src/com/fsck/k9/mail/store/local/LocalStore.java +++ b/src/com/fsck/k9/local/LocalStore.java @@ -1,5 +1,5 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import android.app.Application; import android.content.ContentResolver; @@ -18,15 +18,14 @@ import com.fsck.k9.helper.UrlEncodingHelper; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; +import com.fsck.k9.mail.MessageRetrievalListener; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Store; import com.fsck.k9.mail.store.RemoteStore; -import com.fsck.k9.mail.store.StorageManager; -import com.fsck.k9.mail.store.StorageManager.StorageProvider; +import com.fsck.k9.local.StorageManager.StorageProvider; import com.fsck.k9.mail.store.StoreConfig; -import com.fsck.k9.mail.store.UnavailableStorageException; -import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback; -import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException; +import com.fsck.k9.local.LockableDatabase.DbCallback; +import com.fsck.k9.local.LockableDatabase.WrappedException; import com.fsck.k9.provider.EmailProvider; import com.fsck.k9.provider.EmailProvider.MessageColumns; import com.fsck.k9.search.LocalSearch; @@ -244,7 +243,7 @@ public class LocalStore extends Store implements Serializable { return Preferences.getPreferences(mApplication).getPreferences(); } - public long getSize() throws UnavailableStorageException { + public long getSize() throws MessagingException { final StorageManager storageManager = StorageManager.getInstance(mApplication); @@ -487,7 +486,7 @@ public class LocalStore extends Store implements Serializable { }); } - public void resetVisibleLimits(int visibleLimit) throws UnavailableStorageException { + public void resetVisibleLimits(int visibleLimit) throws MessagingException { final ContentValues cv = new ContentValues(); cv.put("visible_limit", Integer.toString(visibleLimit)); database.execute(false, new DbCallback() { @@ -499,7 +498,7 @@ public class LocalStore extends Store implements Serializable { }); } - public List getPendingCommands() throws UnavailableStorageException { + public List getPendingCommands() throws MessagingException { return database.execute(false, new DbCallback>() { @Override public List doDbWork(final SQLiteDatabase db) throws WrappedException { @@ -532,7 +531,7 @@ public class LocalStore extends Store implements Serializable { }); } - public void addPendingCommand(PendingCommand command) throws UnavailableStorageException { + public void addPendingCommand(PendingCommand command) throws MessagingException { for (int i = 0; i < command.arguments.length; i++) { command.arguments[i] = UrlEncodingHelper.encodeUtf8(command.arguments[i]); } @@ -548,7 +547,7 @@ public class LocalStore extends Store implements Serializable { }); } - public void removePendingCommand(final PendingCommand command) throws UnavailableStorageException { + public void removePendingCommand(final PendingCommand command) throws MessagingException { database.execute(false, new DbCallback() { @Override public Void doDbWork(final SQLiteDatabase db) throws WrappedException { @@ -558,7 +557,7 @@ public class LocalStore extends Store implements Serializable { }); } - public void removePendingCommands() throws UnavailableStorageException { + public void removePendingCommands() throws MessagingException { database.execute(false, new DbCallback() { @Override public Void doDbWork(final SQLiteDatabase db) throws WrappedException { @@ -690,7 +689,7 @@ public class LocalStore extends Store implements Serializable { return searchForMessages(null, search); } - public AttachmentInfo getAttachmentInfo(final String attachmentId) throws UnavailableStorageException { + public AttachmentInfo getAttachmentInfo(final String attachmentId) throws MessagingException { return database.execute(false, new DbCallback() { @Override public AttachmentInfo doDbWork(final SQLiteDatabase db) throws WrappedException { @@ -731,7 +730,7 @@ public class LocalStore extends Store implements Serializable { public String type; } - public void createFolders(final List foldersToCreate, final int visibleLimit) throws UnavailableStorageException { + public void createFolders(final List foldersToCreate, final int visibleLimit) throws MessagingException { database.execute(true, new DbCallback() { @Override public Void doDbWork(final SQLiteDatabase db) throws WrappedException { diff --git a/src/com/fsck/k9/mail/store/local/LocalTextBody.java b/src/com/fsck/k9/local/LocalTextBody.java similarity index 92% rename from src/com/fsck/k9/mail/store/local/LocalTextBody.java rename to src/com/fsck/k9/local/LocalTextBody.java index 3d83c6eb9..b40d79b8e 100644 --- a/src/com/fsck/k9/mail/store/local/LocalTextBody.java +++ b/src/com/fsck/k9/local/LocalTextBody.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import com.fsck.k9.mail.internet.TextBody; diff --git a/src/com/fsck/k9/mail/store/local/LockableDatabase.java b/src/com/fsck/k9/local/LockableDatabase.java similarity index 97% rename from src/com/fsck/k9/mail/store/local/LockableDatabase.java rename to src/com/fsck/k9/local/LockableDatabase.java index 57cae16ef..62f013953 100644 --- a/src/com/fsck/k9/mail/store/local/LockableDatabase.java +++ b/src/com/fsck/k9/local/LockableDatabase.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import java.io.File; import java.util.concurrent.locks.Lock; @@ -16,8 +16,6 @@ import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.helper.FileHelper; import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.mail.store.StorageManager; -import com.fsck.k9.mail.store.UnavailableStorageException; public class LockableDatabase { @@ -35,9 +33,10 @@ public class LockableDatabase { * null. * @return Any relevant data. Can be null. * @throws WrappedException - * @throws com.fsck.k9.mail.store.UnavailableStorageException + * @throws com.fsck.k9.mail.MessagingException + * @throws com.fsck.k9.local.UnavailableStorageException */ - T doDbWork(SQLiteDatabase db) throws WrappedException, UnavailableStorageException; + T doDbWork(SQLiteDatabase db) throws WrappedException, MessagingException; } public static interface SchemaDefinition { @@ -272,7 +271,7 @@ public class LockableDatabase { * @return Whatever {@link DbCallback#doDbWork(SQLiteDatabase)} returns. * @throws UnavailableStorageException */ - public T execute(final boolean transactional, final DbCallback callback) throws UnavailableStorageException { + public T execute(final boolean transactional, final DbCallback callback) throws MessagingException { lockRead(); final boolean doTransaction = transactional && inTransaction.get() == null; try { diff --git a/src/com/fsck/k9/mail/store/local/MessageRemovalListener.java b/src/com/fsck/k9/local/MessageRemovalListener.java similarity index 77% rename from src/com/fsck/k9/mail/store/local/MessageRemovalListener.java rename to src/com/fsck/k9/local/MessageRemovalListener.java index 95e35fd22..3f4d47d0e 100644 --- a/src/com/fsck/k9/mail/store/local/MessageRemovalListener.java +++ b/src/com/fsck/k9/local/MessageRemovalListener.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import com.fsck.k9.mail.Message; diff --git a/src/com/fsck/k9/mail/store/StorageManager.java b/src/com/fsck/k9/local/StorageManager.java similarity index 99% rename from src/com/fsck/k9/mail/store/StorageManager.java rename to src/com/fsck/k9/local/StorageManager.java index 68cc216df..20676d720 100644 --- a/src/com/fsck/k9/mail/store/StorageManager.java +++ b/src/com/fsck/k9/local/StorageManager.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store; +package com.fsck.k9.local; import java.io.File; import java.io.IOException; diff --git a/src/com/fsck/k9/mail/store/local/StoreSchemaDefinition.java b/src/com/fsck/k9/local/StoreSchemaDefinition.java similarity index 99% rename from src/com/fsck/k9/mail/store/local/StoreSchemaDefinition.java rename to src/com/fsck/k9/local/StoreSchemaDefinition.java index aec7390fc..c893e5888 100644 --- a/src/com/fsck/k9/mail/store/local/StoreSchemaDefinition.java +++ b/src/com/fsck/k9/local/StoreSchemaDefinition.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import java.util.ArrayList; import java.util.List; diff --git a/src/com/fsck/k9/mail/store/local/TempFileBody.java b/src/com/fsck/k9/local/TempFileBody.java similarity index 94% rename from src/com/fsck/k9/mail/store/local/TempFileBody.java rename to src/com/fsck/k9/local/TempFileBody.java index 9b7916b5a..c687d6082 100644 --- a/src/com/fsck/k9/mail/store/local/TempFileBody.java +++ b/src/com/fsck/k9/local/TempFileBody.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import java.io.ByteArrayInputStream; import java.io.File; diff --git a/src/com/fsck/k9/mail/store/local/TempFileMessageBody.java b/src/com/fsck/k9/local/TempFileMessageBody.java similarity index 96% rename from src/com/fsck/k9/mail/store/local/TempFileMessageBody.java rename to src/com/fsck/k9/local/TempFileMessageBody.java index 5cd5e207b..55f6b76e4 100644 --- a/src/com/fsck/k9/mail/store/local/TempFileMessageBody.java +++ b/src/com/fsck/k9/local/TempFileMessageBody.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; import java.io.IOException; import java.io.OutputStream; diff --git a/src/com/fsck/k9/mail/store/local/ThreadInfo.java b/src/com/fsck/k9/local/ThreadInfo.java similarity index 92% rename from src/com/fsck/k9/mail/store/local/ThreadInfo.java rename to src/com/fsck/k9/local/ThreadInfo.java index 4115c16d6..cf72dddc1 100644 --- a/src/com/fsck/k9/mail/store/local/ThreadInfo.java +++ b/src/com/fsck/k9/local/ThreadInfo.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.local; class ThreadInfo { public final long threadId; diff --git a/src/com/fsck/k9/mail/store/UnavailableStorageException.java b/src/com/fsck/k9/local/UnavailableStorageException.java similarity index 96% rename from src/com/fsck/k9/mail/store/UnavailableStorageException.java rename to src/com/fsck/k9/local/UnavailableStorageException.java index 90974161b..d6e1cd69d 100644 --- a/src/com/fsck/k9/mail/store/UnavailableStorageException.java +++ b/src/com/fsck/k9/local/UnavailableStorageException.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.store; +package com.fsck.k9.local; import com.fsck.k9.mail.MessagingException; diff --git a/src/com/fsck/k9/mail/Address.java b/src/com/fsck/k9/mail/Address.java index 25929e94e..9702c1c8d 100644 --- a/src/com/fsck/k9/mail/Address.java +++ b/src/com/fsck/k9/mail/Address.java @@ -3,6 +3,7 @@ package com.fsck.k9.mail; import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; import org.apache.james.mime4j.MimeException; import org.apache.james.mime4j.codec.EncoderUtil; @@ -20,7 +21,6 @@ import android.text.util.Rfc822Tokenizer; import android.util.Log; import com.fsck.k9.K9; -import com.fsck.k9.helper.Utility; public class Address { @@ -28,6 +28,8 @@ public class Address { String getNameForAddress(String address); } + private static final Pattern ATOM = Pattern.compile("^(?:[a-zA-Z0-9!#$%&'*+\\-/=?^_`{|}~]|\\s)+$"); + /** * If the number of addresses exceeds this value the addresses aren't * resolved to the names of Android contacts. @@ -201,7 +203,7 @@ public class Address { @Override public String toString() { if (!TextUtils.isEmpty(mPersonal)) { - return Utility.quoteAtoms(mPersonal) + " <" + mAddress + ">"; + return quoteAtoms(mPersonal) + " <" + mAddress + ">"; } else { return mAddress; } @@ -367,4 +369,44 @@ public class Address { } return sb.toString(); } + + /** + * Quote a string, if necessary, based upon the definition of an "atom," as defined by RFC2822 + * (http://tools.ietf.org/html/rfc2822#section-3.2.4). Strings that consist purely of atoms are + * left unquoted; anything else is returned as a quoted string. + * @param text String to quote. + * @return Possibly quoted string. + */ + public static String quoteAtoms(final String text) { + if (ATOM.matcher(text).matches()) { + return text; + } else { + return quoteString(text); + } + } + + /** + * Ensures that the given string starts and ends with the double quote character. The string is not modified in any way except to add the + * double quote character to start and end if it's not already there. + * sample -> "sample" + * "sample" -> "sample" + * ""sample"" -> "sample" + * "sample"" -> "sample" + * sa"mp"le -> "sa"mp"le" + * "sa"mp"le" -> "sa"mp"le" + * (empty string) -> "" + * " -> "" + * @param s + * @return + */ + private static String quoteString(String s) { + if (s == null) { + return null; + } + if (!s.matches("^\".*\"$")) { + return "\"" + s + "\""; + } else { + return s; + } + } } diff --git a/src/com/fsck/k9/mail/Body.java b/src/com/fsck/k9/mail/Body.java index 93960892c..d86746d66 100644 --- a/src/com/fsck/k9/mail/Body.java +++ b/src/com/fsck/k9/mail/Body.java @@ -5,8 +5,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import com.fsck.k9.mail.store.UnavailableStorageException; - public interface Body { /** * Returns the raw data of the body, without transfer encoding etc applied. @@ -18,7 +16,7 @@ public interface Body { /** * Sets the content transfer encoding (7bit, 8bit, quoted-printable or base64). */ - public void setEncoding(String encoding) throws UnavailableStorageException, MessagingException; + public void setEncoding(String encoding) throws MessagingException; /** * Writes the body's data to the given {@link OutputStream}. diff --git a/src/com/fsck/k9/mail/Folder.java b/src/com/fsck/k9/mail/Folder.java index c4f1a3998..a11e7dd32 100644 --- a/src/com/fsck/k9/mail/Folder.java +++ b/src/com/fsck/k9/mail/Folder.java @@ -8,7 +8,6 @@ import java.util.Set; import android.util.Log; import com.fsck.k9.K9; -import com.fsck.k9.mail.store.local.MessageRetrievalListener; public abstract class Folder { private String status = null; diff --git a/src/com/fsck/k9/mail/Message.java b/src/com/fsck/k9/mail/Message.java index d97982a4d..b0bfe12af 100644 --- a/src/com/fsck/k9/mail/Message.java +++ b/src/com/fsck/k9/mail/Message.java @@ -12,7 +12,6 @@ import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.mail.filter.CountingOutputStream; import com.fsck.k9.mail.filter.EOLConvertingOutputStream; -import com.fsck.k9.mail.store.UnavailableStorageException; public abstract class Message implements Part, CompositeBody { @@ -117,28 +116,7 @@ public abstract class Message implements Part, CompositeBody { public abstract void setReferences(String references) throws MessagingException; - @Override - public abstract Body getBody(); - - @Override - public abstract String getContentType() throws MessagingException; - - @Override - public abstract void addHeader(String name, String value) throws MessagingException; - - @Override - public abstract void setHeader(String name, String value) throws MessagingException; - - @Override - public abstract String[] getHeader(String name) throws MessagingException; - - public abstract Set getHeaderNames() throws UnavailableStorageException; - - @Override - public abstract void removeHeader(String name) throws MessagingException; - - @Override - public abstract void setBody(Body body) throws MessagingException; + public abstract Set getHeaderNames() throws MessagingException; public abstract long getId(); @@ -287,6 +265,4 @@ public abstract class Message implements Part, CompositeBody { */ @Override public abstract Message clone(); - @Override - public abstract void setUsing7bitTransport() throws MessagingException; } diff --git a/src/com/fsck/k9/mail/store/local/MessageRetrievalListener.java b/src/com/fsck/k9/mail/MessageRetrievalListener.java similarity index 85% rename from src/com/fsck/k9/mail/store/local/MessageRetrievalListener.java rename to src/com/fsck/k9/mail/MessageRetrievalListener.java index 12679b4e5..0d6b4fff1 100644 --- a/src/com/fsck/k9/mail/store/local/MessageRetrievalListener.java +++ b/src/com/fsck/k9/mail/MessageRetrievalListener.java @@ -1,7 +1,6 @@ -package com.fsck.k9.mail.store.local; +package com.fsck.k9.mail; -import com.fsck.k9.mail.Message; public interface MessageRetrievalListener { public void messageStarted(String uid, int number, int ofTotal); diff --git a/src/com/fsck/k9/mail/ServerSettings.java b/src/com/fsck/k9/mail/ServerSettings.java index 43e16847b..59633ffdd 100644 --- a/src/com/fsck/k9/mail/ServerSettings.java +++ b/src/com/fsck/k9/mail/ServerSettings.java @@ -3,7 +3,6 @@ package com.fsck.k9.mail; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import com.fsck.k9.Account; /** * This is an abstraction to get rid of the store- and transport-specific URIs. @@ -13,8 +12,8 @@ import com.fsck.k9.Account; * store/transport URIs altogether. *

    * - * @see Account#getStoreUri() - * @see Account#getTransportUri() + * @see com.fsck.k9.mail.store.StoreConfig#getStoreUri() + * @see com.fsck.k9.mail.store.StoreConfig#getTransportUri() */ public class ServerSettings { /** diff --git a/src/com/fsck/k9/mail/Transport.java b/src/com/fsck/k9/mail/Transport.java index 28011dacd..97e4ada1d 100644 --- a/src/com/fsck/k9/mail/Transport.java +++ b/src/com/fsck/k9/mail/Transport.java @@ -1,7 +1,7 @@ package com.fsck.k9.mail; -import com.fsck.k9.Account; +import com.fsck.k9.mail.store.StoreConfig; import com.fsck.k9.mail.transport.SmtpTransport; import com.fsck.k9.mail.transport.WebDavTransport; @@ -11,12 +11,12 @@ public abstract class Transport { // RFC 1047 protected static final int SOCKET_READ_TIMEOUT = 300000; - public synchronized static Transport getInstance(Account account) throws MessagingException { - String uri = account.getTransportUri(); + public synchronized static Transport getInstance(StoreConfig storeConfig) throws MessagingException { + String uri = storeConfig.getTransportUri(); if (uri.startsWith("smtp")) { - return new SmtpTransport(account); + return new SmtpTransport(storeConfig); } else if (uri.startsWith("webdav")) { - return new WebDavTransport(account); + return new WebDavTransport(storeConfig); } else { throw new MessagingException("Unable to locate an applicable Transport for " + uri); } diff --git a/src/com/fsck/k9/mail/filter/Base64.java b/src/com/fsck/k9/mail/filter/Base64.java index 934c3b222..9f7cadfa5 100644 --- a/src/com/fsck/k9/mail/filter/Base64.java +++ b/src/com/fsck/k9/mail/filter/Base64.java @@ -34,6 +34,22 @@ import java.nio.charset.Charset; * @version $Id$ */ public class Base64 { + public static String decode(String encoded) { + if (encoded == null) { + return null; + } + byte[] decoded = new Base64().decode(encoded.getBytes()); + return new String(decoded); + } + + public static String encode(String s) { + if (s == null) { + return null; + } + byte[] encoded = new Base64().encode(s.getBytes()); + return new String(encoded); + } + /** * Chunk size per RFC 2045 section 6.8. * diff --git a/src/com/fsck/k9/mail/internet/HtmlConverter.java b/src/com/fsck/k9/mail/internet/HtmlConverter.java index a217d5b81..8d28085c1 100644 --- a/src/com/fsck/k9/mail/internet/HtmlConverter.java +++ b/src/com/fsck/k9/mail/internet/HtmlConverter.java @@ -4,7 +4,6 @@ import android.text.*; import android.text.Html.TagHandler; import android.util.Log; import com.fsck.k9.K9; -import com.fsck.k9.helper.Regex; import org.xml.sax.XMLReader; @@ -15,11 +14,75 @@ import java.util.HashSet; import java.util.Locale; import java.util.Set; import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Contains common routines to convert html to text and vice versa. */ public class HtmlConverter { + /* This comprises most common used Unicode characters allowed in IRI + * as detailed in RFC 3987. + * Specifically, those two byte Unicode characters are not included. + */ + private static final String GOOD_IRI_CHAR = + "a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"; + /** + * Goegular expression to match all IANA top-level domains for WEB_URL. + * List accurate as of 2011/01/12. List taken from: + * http://data.iana.org/TLD/tlds-alpha-by-domain.txt + * This pattern is auto-generated by frameworks/base/common/tools/make-iana-tld-pattern.py + */ + private static final String TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL = + "(?:" + + "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])" + + "|(?:biz|b[abdefghijmnorstvwyz])" + + "|(?:cat|com|coop|c[acdfghiklmnoruvxyz])" + + "|d[ejkmoz]" + + "|(?:edu|e[cegrstu])" + + "|f[ijkmor]" + + "|(?:gov|g[abdefghilmnpqrstuwy])" + + "|h[kmnrtu]" + + "|(?:info|int|i[delmnoqrst])" + + "|(?:jobs|j[emop])" + + "|k[eghimnprwyz]" + + "|l[abcikrstuvy]" + + "|(?:mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])" + + "|(?:name|net|n[acefgilopruz])" + + "|(?:org|om)" + + "|(?:pro|p[aefghklmnrstwy])" + + "|qa" + + "|r[eosuw]" + + "|s[abcdeghijklmnortuvyz]" + + "|(?:tel|travel|t[cdfghjklmnoprtvwz])" + + "|u[agksyz]" + + "|v[aceginu]" + + "|w[fs]" + + "|(?:xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s|xn\\-\\-fzc2c9e2c|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-j6w193g|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-kprw13d|xn\\-\\-kpry57d|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbayh7gpa|xn\\-\\-mgberp4a5d4ar|xn\\-\\-o3cw4h|xn\\-\\-p1ai|xn\\-\\-pgbs0dh|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a|xn\\-\\-xkc2al3hye2a|xn\\-\\-ygbi2ammx|xn\\-\\-zckzah)" + + "|y[et]" + + "|z[amw]))"; + private static final String BITCOIN_URI_PATTERN = + "bitcoin:[1-9a-km-zA-HJ-NP-Z]{27,34}(\\?[a-zA-Z0-9$\\-_.+!*'(),%:@&=]*)?"; + /** + * Regular expression pattern to match most part of RFC 3987 + * Internationalized URLs, aka IRIs. Commonly used Unicode characters are + * added. + */ + private static final Pattern WEB_URL_PATTERN = Pattern.compile( + "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)" + + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_" + + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?" + + "((?:(?:[" + GOOD_IRI_CHAR + "][" + GOOD_IRI_CHAR + "\\-]{0,64}\\.)+" // named host + + TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL + + "|(?:(?:25[0-5]|2[0-4]" // or ip address + + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]" + + "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]" + + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}" + + "|[1-9][0-9]|[0-9])))" + + "(?:\\:\\d{1,5})?)" // plus option port number + + "(\\/(?:(?:[" + GOOD_IRI_CHAR + "\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus option query params + + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?" + + "(?:\\b|$)"); // and finally, a word boundary or end of + /** * When generating previews, Spannable objects that can't be converted into a String are * represented as 0xfffc. When displayed, these show up as undisplayed squares. These constants @@ -383,9 +446,9 @@ public class HtmlConverter { * @param outputBuffer Buffer to append linked text to. */ protected static void linkifyText(final String text, final StringBuffer outputBuffer) { - String prepared = text.replaceAll(Regex.BITCOIN_URI_PATTERN, "$0"); + String prepared = text.replaceAll(BITCOIN_URI_PATTERN, "$0"); - Matcher m = Regex.WEB_URL_PATTERN.matcher(prepared); + Matcher m = WEB_URL_PATTERN.matcher(prepared); while (m.find()) { int start = m.start(); if (start == 0 || (start != 0 && prepared.charAt(start - 1) != '@')) { diff --git a/src/com/fsck/k9/mail/internet/MimeHeader.java b/src/com/fsck/k9/mail/internet/MimeHeader.java index 12bd5fd31..7c9569df0 100644 --- a/src/com/fsck/k9/mail/internet/MimeHeader.java +++ b/src/com/fsck/k9/mail/internet/MimeHeader.java @@ -1,8 +1,6 @@ package com.fsck.k9.mail.internet; -import com.fsck.k9.helper.Utility; - import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; @@ -97,7 +95,7 @@ public class MimeHeader { public void writeTo(OutputStream out) throws IOException { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024); for (Field field : mFields) { - if (!Utility.arrayContains(writeOmitFields, field.name)) { + if (!Arrays.asList(writeOmitFields).contains(field.name)) { String v = field.value; if (hasToBeEncoded(v)) { diff --git a/src/com/fsck/k9/mail/internet/MimeMessage.java b/src/com/fsck/k9/mail/internet/MimeMessage.java index 0d7bb8bab..02c028edf 100644 --- a/src/com/fsck/k9/mail/internet/MimeMessage.java +++ b/src/com/fsck/k9/mail/internet/MimeMessage.java @@ -33,7 +33,6 @@ import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Multipart; import com.fsck.k9.mail.Part; -import com.fsck.k9.mail.store.UnavailableStorageException; import com.fsck.k9.K9; /** @@ -333,7 +332,7 @@ public class MimeMessage extends Message { return "<" + UUID.randomUUID().toString().toUpperCase(Locale.US) + "@" + hostname + ">"; } - public void setMessageId(String messageId) throws UnavailableStorageException { + public void setMessageId(String messageId) throws MessagingException { setHeader("Message-ID", messageId); mMessageId = messageId; } @@ -419,27 +418,27 @@ public class MimeMessage extends Message { } @Override - public void addHeader(String name, String value) throws UnavailableStorageException { + public void addHeader(String name, String value) throws MessagingException { mHeader.addHeader(name, value); } @Override - public void setHeader(String name, String value) throws UnavailableStorageException { + public void setHeader(String name, String value) throws MessagingException { mHeader.setHeader(name, value); } @Override - public String[] getHeader(String name) throws UnavailableStorageException { + public String[] getHeader(String name) throws MessagingException { return mHeader.getHeader(name); } @Override - public void removeHeader(String name) throws UnavailableStorageException { + public void removeHeader(String name) throws MessagingException { mHeader.removeHeader(name); } @Override - public Set getHeaderNames() throws UnavailableStorageException { + public Set getHeaderNames() throws MessagingException { return mHeader.getHeaderNames(); } diff --git a/src/com/fsck/k9/security/LocalKeyStore.java b/src/com/fsck/k9/mail/ssl/LocalKeyStore.java similarity index 99% rename from src/com/fsck/k9/security/LocalKeyStore.java rename to src/com/fsck/k9/mail/ssl/LocalKeyStore.java index d432ab294..7133be7b0 100644 --- a/src/com/fsck/k9/security/LocalKeyStore.java +++ b/src/com/fsck/k9/mail/ssl/LocalKeyStore.java @@ -1,4 +1,4 @@ -package com.fsck.k9.security; +package com.fsck.k9.mail.ssl; import java.io.File; import java.io.FileInputStream; diff --git a/src/com/fsck/k9/mail/ssl/TrustManagerFactory.java b/src/com/fsck/k9/mail/ssl/TrustManagerFactory.java index c1956011c..f6841a2e3 100644 --- a/src/com/fsck/k9/mail/ssl/TrustManagerFactory.java +++ b/src/com/fsck/k9/mail/ssl/TrustManagerFactory.java @@ -4,7 +4,6 @@ package com.fsck.k9.mail.ssl; import android.util.Log; import com.fsck.k9.mail.CertificateChainException; -import com.fsck.k9.security.LocalKeyStore; import javax.net.ssl.SSLException; import javax.net.ssl.TrustManager; diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java index b8be10039..051c29f2d 100644 --- a/src/com/fsck/k9/mail/store/ImapStore.java +++ b/src/com/fsck/k9/mail/store/ImapStore.java @@ -58,7 +58,6 @@ import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.R; import com.fsck.k9.helper.UrlEncodingHelper; -import com.fsck.k9.helper.Utility; import com.fsck.k9.helper.power.TracingPowerManager; import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock; import com.fsck.k9.mail.AuthType; @@ -71,7 +70,7 @@ import com.fsck.k9.mail.FetchProfile; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; -import com.fsck.k9.mail.store.local.MessageRetrievalListener; +import com.fsck.k9.mail.MessageRetrievalListener; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.PushReceiver; @@ -88,7 +87,6 @@ import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.store.ImapResponseParser.ImapList; import com.fsck.k9.mail.store.ImapResponseParser.ImapResponse; -import com.fsck.k9.mail.store.imap.ImapUtility; import com.fsck.k9.mail.transport.imap.ImapSettings; import com.fsck.k9.mail.ssl.TrustedSocketFactory; @@ -246,7 +244,7 @@ public class ImapStore extends RemoteStore { * * @return An ImapStore URI that holds the same information as the {@code server} parameter. * - * @see Account#getStoreUri() + * @see com.fsck.k9.mail.store.StoreConfig#getStoreUri() * @see ImapStore#decodeUri(String) */ public static String createUri(ServerSettings server) { @@ -1125,7 +1123,7 @@ public class ImapStore extends RemoteStore { //TODO: Split this into multiple commands if the command exceeds a certain length. List responses = executeSimpleCommand(String.format("UID COPY %s %s", - Utility.combine(uids, ','), + combine(uids, ','), remoteDestName)); // Get the tagged response for the UID COPY command @@ -1332,7 +1330,7 @@ public class ImapStore extends RemoteStore { ImapSearcher searcher = new ImapSearcher() { @Override public List search() throws IOException, MessagingException { - return executeSimpleCommand(String.format("UID SEARCH %s%s", Utility.combine(mesgSeqs.toArray(), ','), includeDeleted ? "" : " NOT DELETED")); + return executeSimpleCommand(String.format("UID SEARCH %s%s", combine(mesgSeqs.toArray(), ','), includeDeleted ? "" : " NOT DELETED")); } }; return search(searcher, listener); @@ -1343,7 +1341,7 @@ public class ImapStore extends RemoteStore { ImapSearcher searcher = new ImapSearcher() { @Override public List search() throws IOException, MessagingException { - return executeSimpleCommand(String.format("UID SEARCH UID %s%s", Utility.combine(mesgUids.toArray(), ','), includeDeleted ? "" : " NOT DELETED")); + return executeSimpleCommand(String.format("UID SEARCH UID %s%s", combine(mesgUids.toArray(), ','), includeDeleted ? "" : " NOT DELETED")); } }; return search(searcher, listener); @@ -1483,8 +1481,8 @@ public class ImapStore extends RemoteStore { try { mConnection.sendCommand(String.format("UID FETCH %s (%s)", - Utility.combine(uidWindow.toArray(new String[uidWindow.size()]), ','), - Utility.combine(fetchFields.toArray(new String[fetchFields.size()]), ' ') + combine(uidWindow.toArray(new String[uidWindow.size()]), ','), + combine(fetchFields.toArray(new String[fetchFields.size()]), ' ') ), false); ImapResponse response; int messageNumber = 0; @@ -2101,7 +2099,7 @@ public class ImapStore extends RemoteStore { } } - return Utility.combine(flagNames.toArray(new String[flagNames.size()]), ' '); + return combine(flagNames.toArray(new String[flagNames.size()]), ' '); } @@ -2151,7 +2149,7 @@ public class ImapStore extends RemoteStore { } try { executeSimpleCommand(String.format("UID STORE %s %sFLAGS.SILENT (%s)", - Utility.combine(uids, ','), + combine(uids, ','), value ? "+" : "-", combineFlags(flags))); } catch (IOException ioe) { @@ -2701,7 +2699,7 @@ public class ImapStore extends RemoteStore { try { receiveCapabilities(executeSimpleCommand( String.format("AUTHENTICATE EXTERNAL %s", - Utility.base64Encode(mSettings.getUsername())), false)); + Base64.encode(mSettings.getUsername())), false)); } catch (ImapException e) { /* * Provide notification to the user of a problem authenticating @@ -3599,4 +3597,11 @@ public class ImapStore extends RemoteStore { return null; } } + + private static String combine(Object[] parts, char separator) { + if (parts == null) { + return null; + } + return TextUtils.join(String.valueOf(separator), parts); + } } diff --git a/src/com/fsck/k9/mail/store/imap/ImapUtility.java b/src/com/fsck/k9/mail/store/ImapUtility.java similarity index 98% rename from src/com/fsck/k9/mail/store/imap/ImapUtility.java rename to src/com/fsck/k9/mail/store/ImapUtility.java index 51d1aa070..971f098cd 100644 --- a/src/com/fsck/k9/mail/store/imap/ImapUtility.java +++ b/src/com/fsck/k9/mail/store/ImapUtility.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.fsck.k9.mail.store.imap; +package com.fsck.k9.mail.store; import android.util.Log; @@ -27,7 +27,7 @@ import java.util.List; /** * Utility methods for use with IMAP. */ -public class ImapUtility { +class ImapUtility { /** * Gets all of the values in a sequence set per RFC 3501. * diff --git a/src/com/fsck/k9/mail/store/Pop3Store.java b/src/com/fsck/k9/mail/store/Pop3Store.java index 0871579b1..85c73f0f0 100644 --- a/src/com/fsck/k9/mail/store/Pop3Store.java +++ b/src/com/fsck/k9/mail/store/Pop3Store.java @@ -6,13 +6,12 @@ import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.R; import com.fsck.k9.helper.UrlEncodingHelper; -import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.*; import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.filter.Hex; import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.ssl.TrustedSocketFactory; -import com.fsck.k9.mail.store.local.MessageRetrievalListener; +import com.fsck.k9.mail.MessageRetrievalListener; import javax.net.ssl.SSLException; @@ -459,7 +458,7 @@ public class Pop3Store extends RemoteStore { try { executeSimpleCommand( String.format("AUTH EXTERNAL %s", - Utility.base64Encode(mUsername)), false); + Base64.encode(mUsername)), false); } catch (Pop3ErrorResponse e) { /* * Provide notification to the user of a problem authenticating diff --git a/src/com/fsck/k9/mail/store/WebDavStore.java b/src/com/fsck/k9/mail/store/WebDavStore.java index 300a68d18..a08d11cbe 100644 --- a/src/com/fsck/k9/mail/store/WebDavStore.java +++ b/src/com/fsck/k9/mail/store/WebDavStore.java @@ -5,11 +5,11 @@ import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.helper.UrlEncodingHelper; -import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.*; +import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.filter.EOLConvertingOutputStream; import com.fsck.k9.mail.internet.MimeMessage; -import com.fsck.k9.mail.store.local.MessageRetrievalListener; +import com.fsck.k9.mail.MessageRetrievalListener; import org.apache.commons.io.IOUtils; import org.apache.http.*; @@ -339,7 +339,7 @@ public class WebDavStore extends RemoteStore { // The inbox path would look like: "https://mail.domain.com/Exchange/alias/Inbox". mUrl = getRoot() + mPath + mMailboxPath; - mAuthString = "Basic " + Utility.base64Encode(mUsername + ":" + mPassword); + mAuthString = "Basic " + Base64.encode(mUsername + ":" + mPassword); } private String getRoot() { diff --git a/src/com/fsck/k9/mail/transport/SmtpTransport.java b/src/com/fsck/k9/mail/transport/SmtpTransport.java index 4e7959f7f..6e30eab20 100644 --- a/src/com/fsck/k9/mail/transport/SmtpTransport.java +++ b/src/com/fsck/k9/mail/transport/SmtpTransport.java @@ -3,20 +3,18 @@ package com.fsck.k9.mail.transport; import android.util.Log; -import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.R; import com.fsck.k9.helper.UrlEncodingHelper; -import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.*; import com.fsck.k9.mail.Message.RecipientType; +import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.filter.EOLConvertingOutputStream; import com.fsck.k9.mail.filter.LineWrapOutputStream; import com.fsck.k9.mail.filter.PeekableInputStream; import com.fsck.k9.mail.filter.SmtpDataStuffing; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.store.StoreConfig; -import com.fsck.k9.mail.store.local.LocalMessage; import com.fsck.k9.mail.ssl.TrustedSocketFactory; import javax.net.ssl.SSLException; @@ -125,7 +123,7 @@ public class SmtpTransport extends Transport { * * @return A SmtpTransport URI that holds the same information as the {@code server} parameter. * - * @see Account#getTransportUri() + * @see com.fsck.k9.mail.store.StoreConfig#getTransportUri() * @see SmtpTransport#decodeUri(String) */ public static String createUri(ServerSettings server) { @@ -150,7 +148,7 @@ public class SmtpTransport extends Transport { break; } - String userInfo = null; + String userInfo; AuthType authType = server.authenticationType; // NOTE: authType is append at last item, in contrast to ImapStore and Pop3Store! if (authType != null) { @@ -184,10 +182,10 @@ public class SmtpTransport extends Transport { private boolean m8bitEncodingAllowed; private int mLargestAcceptableMessage; - public SmtpTransport(StoreConfig account) throws MessagingException { + public SmtpTransport(StoreConfig storeConfig) throws MessagingException { ServerSettings settings; try { - settings = decodeUri(account.getTransportUri()); + settings = decodeUri(storeConfig.getTransportUri()); } catch (IllegalArgumentException e) { throw new MessagingException("Error while decoding transport URI", e); } @@ -496,7 +494,7 @@ public class SmtpTransport extends Transport { } // If the message has attachments and our server has told us about a limit on // the size of messages, count the message's size before sending it - if (mLargestAcceptableMessage > 0 && ((LocalMessage)message).hasAttachments()) { + if (mLargestAcceptableMessage > 0 && message.hasAttachments()) { if (message.calculateSize() > mLargestAcceptableMessage) { MessagingException me = new MessagingException("Message too large for server"); //TODO this looks rather suspicious... shouldn't it be true? @@ -702,8 +700,8 @@ public class SmtpTransport extends Transport { AuthenticationFailedException, IOException { try { executeSimpleCommand("AUTH LOGIN"); - executeSimpleCommand(Utility.base64Encode(username), true); - executeSimpleCommand(Utility.base64Encode(password), true); + executeSimpleCommand(Base64.encode(username), true); + executeSimpleCommand(Base64.encode(password), true); } catch (NegativeSmtpReplyException exception) { if (exception.getReplyCode() == 535) { // Authentication credentials invalid @@ -717,7 +715,7 @@ public class SmtpTransport extends Transport { private void saslAuthPlain(String username, String password) throws MessagingException, AuthenticationFailedException, IOException { - String data = Utility.base64Encode("\000" + username + "\000" + password); + String data = Base64.encode("\000" + username + "\000" + password); try { executeSimpleCommand("AUTH PLAIN " + data, true); } catch (NegativeSmtpReplyException exception) { @@ -757,7 +755,7 @@ public class SmtpTransport extends Transport { private void saslAuthExternal(String username) throws MessagingException, IOException { executeSimpleCommand( String.format("AUTH EXTERNAL %s", - Utility.base64Encode(username)), false); + Base64.encode(username)), false); } /** diff --git a/src/com/fsck/k9/preferences/AccountSettings.java b/src/com/fsck/k9/preferences/AccountSettings.java index c2b6b4983..327ff38b2 100644 --- a/src/com/fsck/k9/preferences/AccountSettings.java +++ b/src/com/fsck/k9/preferences/AccountSettings.java @@ -13,7 +13,7 @@ import com.fsck.k9.Account.SortType; import com.fsck.k9.K9; import com.fsck.k9.R; import com.fsck.k9.Account.FolderMode; -import com.fsck.k9.mail.store.StorageManager; +import com.fsck.k9.local.StorageManager; import com.fsck.k9.preferences.Settings.*; public class AccountSettings { diff --git a/src/com/fsck/k9/preferences/SettingsImporter.java b/src/com/fsck/k9/preferences/SettingsImporter.java index 1a7af8d26..1d05cf9dc 100644 --- a/src/com/fsck/k9/preferences/SettingsImporter.java +++ b/src/com/fsck/k9/preferences/SettingsImporter.java @@ -22,11 +22,11 @@ import com.fsck.k9.Account; import com.fsck.k9.Identity; import com.fsck.k9.K9; import com.fsck.k9.Preferences; -import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.AuthType; import com.fsck.k9.mail.ConnectionSecurity; import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.Transport; +import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.store.RemoteStore; import com.fsck.k9.mail.store.WebDavStore; import com.fsck.k9.preferences.Settings.InvalidSettingValueException; @@ -377,7 +377,7 @@ public class SettingsImporter { // Write incoming server settings (storeUri) ServerSettings incoming = new ImportedServerSettings(account.incoming); String storeUri = RemoteStore.createStoreUri(incoming); - putString(editor, accountKeyPrefix + Account.STORE_URI_KEY, Utility.base64Encode(storeUri)); + putString(editor, accountKeyPrefix + Account.STORE_URI_KEY, Base64.encode(storeUri)); // Mark account as disabled if the AuthType isn't EXTERNAL and the // settings file didn't contain a password @@ -393,7 +393,7 @@ public class SettingsImporter { // Write outgoing server settings (transportUri) ServerSettings outgoing = new ImportedServerSettings(account.outgoing); String transportUri = Transport.createTransportUri(outgoing); - putString(editor, accountKeyPrefix + Account.TRANSPORT_URI_KEY, Utility.base64Encode(transportUri)); + putString(editor, accountKeyPrefix + Account.TRANSPORT_URI_KEY, Base64.encode(transportUri)); /* * Mark account as disabled if the settings file contained a diff --git a/src/com/fsck/k9/preferences/Storage.java b/src/com/fsck/k9/preferences/Storage.java index 88260f7ff..1303fc8e9 100644 --- a/src/com/fsck/k9/preferences/Storage.java +++ b/src/com/fsck/k9/preferences/Storage.java @@ -11,9 +11,9 @@ import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.helper.UrlEncodingHelper; import com.fsck.k9.helper.Utility; +import com.fsck.k9.mail.filter.Base64; import java.net.URI; -import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -54,8 +54,8 @@ public class Storage implements SharedPreferences { String[] uuids = accountUuids.split(","); for (String uuid : uuids) { try { - String storeUriStr = Utility.base64Decode(readValue(mDb, uuid + ".storeUri")); - String transportUriStr = Utility.base64Decode(readValue(mDb, uuid + ".transportUri")); + String storeUriStr = Base64.decode(readValue(mDb, uuid + ".storeUri")); + String transportUriStr = Base64.decode(readValue(mDb, uuid + ".transportUri")); URI uri = new URI(transportUriStr); String newUserInfo = null; @@ -77,7 +77,7 @@ public class Storage implements SharedPreferences { if (newUserInfo != null) { URI newUri = new URI(uri.getScheme(), newUserInfo, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment()); - String newTransportUriStr = Utility.base64Encode(newUri.toString()); + String newTransportUriStr = Base64.encode(newUri.toString()); writeValue(mDb, uuid + ".transportUri", newTransportUriStr); } @@ -121,7 +121,7 @@ public class Storage implements SharedPreferences { if (newUserInfo != null) { URI newUri = new URI(uri.getScheme(), newUserInfo, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment()); - String newStoreUriStr = Utility.base64Encode(newUri.toString()); + String newStoreUriStr = Base64.encode(newUri.toString()); writeValue(mDb, uuid + ".storeUri", newStoreUriStr); } } catch (Exception e) { diff --git a/src/com/fsck/k9/provider/AttachmentProvider.java b/src/com/fsck/k9/provider/AttachmentProvider.java index bfcdaac9b..792c8885f 100644 --- a/src/com/fsck/k9/provider/AttachmentProvider.java +++ b/src/com/fsck/k9/provider/AttachmentProvider.java @@ -15,9 +15,9 @@ import com.fsck.k9.K9; import com.fsck.k9.Preferences; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.internet.MimeUtility; -import com.fsck.k9.mail.store.local.LocalStore; -import com.fsck.k9.mail.store.local.LocalStore.AttachmentInfo; -import com.fsck.k9.mail.store.StorageManager; +import com.fsck.k9.local.LocalStore; +import com.fsck.k9.local.LocalStore.AttachmentInfo; +import com.fsck.k9.local.StorageManager; import java.io.*; import java.util.List; diff --git a/src/com/fsck/k9/provider/EmailProvider.java b/src/com/fsck/k9/provider/EmailProvider.java index 0164ce4ec..fb025f46c 100644 --- a/src/com/fsck/k9/provider/EmailProvider.java +++ b/src/com/fsck/k9/provider/EmailProvider.java @@ -10,11 +10,11 @@ import com.fsck.k9.Preferences; import com.fsck.k9.cache.EmailProviderCacheCursor; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.mail.store.local.LockableDatabase; -import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback; -import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException; -import com.fsck.k9.mail.store.UnavailableStorageException; -import com.fsck.k9.mail.store.local.LocalStore; +import com.fsck.k9.local.LockableDatabase; +import com.fsck.k9.local.LockableDatabase.DbCallback; +import com.fsck.k9.local.LockableDatabase.WrappedException; +import com.fsck.k9.local.UnavailableStorageException; +import com.fsck.k9.local.LocalStore; import com.fsck.k9.search.SqlQueryBuilder; import android.content.ContentProvider; @@ -357,6 +357,8 @@ public class EmailProvider extends ContentProvider { }); } catch (UnavailableStorageException e) { throw new RuntimeException("Storage not available", e); + } catch (MessagingException e) { + throw new RuntimeException("messaging exception", e); } } @@ -427,6 +429,8 @@ public class EmailProvider extends ContentProvider { }); } catch (UnavailableStorageException e) { throw new RuntimeException("Storage not available", e); + } catch (MessagingException e) { + throw new RuntimeException("messaging exception", e); } } @@ -532,7 +536,10 @@ public class EmailProvider extends ContentProvider { }); } catch (UnavailableStorageException e) { throw new RuntimeException("Storage not available", e); + } catch (MessagingException e) { + throw new RuntimeException("messaging exception", e); } + } private Cursor getAccountStats(String accountUuid, String[] columns, @@ -594,7 +601,10 @@ public class EmailProvider extends ContentProvider { }); } catch (UnavailableStorageException e) { throw new RuntimeException("Storage not available", e); + } catch (MessagingException e) { + throw new RuntimeException("messaging exception", e); } + } private Account getAccount(String accountUuid) { diff --git a/src/com/fsck/k9/provider/MessageProvider.java b/src/com/fsck/k9/provider/MessageProvider.java index c75f017df..a02bf5520 100644 --- a/src/com/fsck/k9/provider/MessageProvider.java +++ b/src/com/fsck/k9/provider/MessageProvider.java @@ -32,9 +32,9 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.mail.store.local.LocalFolder; -import com.fsck.k9.mail.store.local.LocalMessage; -import com.fsck.k9.mail.store.local.LocalStore; +import com.fsck.k9.local.LocalFolder; +import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.local.LocalStore; import com.fsck.k9.search.SearchAccount; import java.lang.ref.WeakReference; diff --git a/src/com/fsck/k9/search/SqlQueryBuilder.java b/src/com/fsck/k9/search/SqlQueryBuilder.java index 1b6ecf6a2..8286f5d9e 100644 --- a/src/com/fsck/k9/search/SqlQueryBuilder.java +++ b/src/com/fsck/k9/search/SqlQueryBuilder.java @@ -5,8 +5,8 @@ import java.util.List; import com.fsck.k9.Account; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Folder; -import com.fsck.k9.mail.store.local.LocalFolder; -import com.fsck.k9.mail.store.local.LocalStore; +import com.fsck.k9.local.LocalFolder; +import com.fsck.k9.local.LocalStore; import com.fsck.k9.search.SearchSpecification.Attribute; import com.fsck.k9.search.SearchSpecification.SearchCondition; import com.fsck.k9.search.SearchSpecification.Searchfield; diff --git a/src/com/fsck/k9/service/DatabaseUpgradeService.java b/src/com/fsck/k9/service/DatabaseUpgradeService.java index ee5b9b26b..15f0e16f1 100644 --- a/src/com/fsck/k9/service/DatabaseUpgradeService.java +++ b/src/com/fsck/k9/service/DatabaseUpgradeService.java @@ -17,7 +17,7 @@ import com.fsck.k9.Preferences; import com.fsck.k9.activity.UpgradeDatabases; import com.fsck.k9.helper.power.TracingPowerManager; import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock; -import com.fsck.k9.mail.store.UnavailableStorageException; +import com.fsck.k9.local.UnavailableStorageException; /** * Service used to upgrade the accounts' databases and/or track the progress of the upgrade. diff --git a/src/com/fsck/k9/service/NotificationActionService.java b/src/com/fsck/k9/service/NotificationActionService.java index 079222e69..96d3cf705 100644 --- a/src/com/fsck/k9/service/NotificationActionService.java +++ b/src/com/fsck/k9/service/NotificationActionService.java @@ -11,8 +11,7 @@ import com.fsck.k9.activity.MessageCompose; import com.fsck.k9.activity.MessageReference; import com.fsck.k9.controller.MessagingController; import com.fsck.k9.mail.Flag; -import com.fsck.k9.mail.Message; -import com.fsck.k9.mail.store.local.LocalMessage; +import com.fsck.k9.local.LocalMessage; import android.app.PendingIntent; import android.content.Context; diff --git a/src/com/fsck/k9/service/StorageGoneReceiver.java b/src/com/fsck/k9/service/StorageGoneReceiver.java index 43f9e49d7..022ae9b9c 100644 --- a/src/com/fsck/k9/service/StorageGoneReceiver.java +++ b/src/com/fsck/k9/service/StorageGoneReceiver.java @@ -7,7 +7,7 @@ import android.net.Uri; import android.util.Log; import com.fsck.k9.K9; -import com.fsck.k9.mail.store.StorageManager; +import com.fsck.k9.local.StorageManager; /** * That BroadcastReceiver is only interested in UNMOUNT events. diff --git a/src/com/fsck/k9/service/StorageReceiver.java b/src/com/fsck/k9/service/StorageReceiver.java index 515750be4..1cf116dd6 100644 --- a/src/com/fsck/k9/service/StorageReceiver.java +++ b/src/com/fsck/k9/service/StorageReceiver.java @@ -7,7 +7,7 @@ import android.net.Uri; import android.util.Log; import com.fsck.k9.K9; -import com.fsck.k9.mail.store.StorageManager; +import com.fsck.k9.local.StorageManager; /** * That BroadcastReceiver is only interested in MOUNT events. diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java index ea917f334..84d0d9ff8 100644 --- a/src/com/fsck/k9/view/AttachmentView.java +++ b/src/com/fsck/k9/view/AttachmentView.java @@ -43,7 +43,7 @@ import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MimeHeader; import com.fsck.k9.mail.internet.MimeUtility; -import com.fsck.k9.mail.store.local.LocalAttachmentBodyPart; +import com.fsck.k9.local.LocalAttachmentBodyPart; import com.fsck.k9.provider.AttachmentProvider; import org.apache.commons.io.IOUtils; diff --git a/src/com/fsck/k9/view/SingleMessageView.java b/src/com/fsck/k9/view/SingleMessageView.java index a351e7921..4747415ef 100644 --- a/src/com/fsck/k9/view/SingleMessageView.java +++ b/src/com/fsck/k9/view/SingleMessageView.java @@ -56,8 +56,8 @@ import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Multipart; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MimeUtility; -import com.fsck.k9.mail.store.local.LocalAttachmentBodyPart; -import com.fsck.k9.mail.store.local.LocalMessage; +import com.fsck.k9.local.LocalAttachmentBodyPart; +import com.fsck.k9.local.LocalMessage; import com.fsck.k9.provider.AttachmentProvider.AttachmentProviderColumns; import org.apache.commons.io.IOUtils; diff --git a/tests/src/com/fsck/k9/helper/Address.java b/tests/src/com/fsck/k9/mail/AddressTest.java similarity index 74% rename from tests/src/com/fsck/k9/helper/Address.java rename to tests/src/com/fsck/k9/mail/AddressTest.java index 250cad347..e1a3778e1 100644 --- a/tests/src/com/fsck/k9/helper/Address.java +++ b/tests/src/com/fsck/k9/mail/AddressTest.java @@ -1,14 +1,14 @@ -package com.fsck.k9.helper; +package com.fsck.k9.mail; import junit.framework.TestCase; -public class Address extends TestCase { +public class AddressTest extends TestCase { /** * test the possibility to parse "From:" fields with no email. * for example: From: News for Vector Limited - Google Finance * http://code.google.com/p/k9mail/issues/detail?id=3814 */ public void testParseWithMissingEmail() { - com.fsck.k9.mail.Address[] addresses = com.fsck.k9.mail.Address.parse("NAME ONLY"); + Address[] addresses = Address.parse("NAME ONLY"); assertEquals(1, addresses.length); assertEquals(null, addresses[0].getAddress()); assertEquals("NAME ONLY", addresses[0].getPersonal()); @@ -18,7 +18,7 @@ public class Address extends TestCase { * test name + valid email */ public void testPraseWithValidEmail() { - com.fsck.k9.mail.Address[] addresses = com.fsck.k9.mail.Address.parse("Max Mustermann "); + Address[] addresses = Address.parse("Max Mustermann "); assertEquals(1, addresses.length); assertEquals("maxmuster@mann.com", addresses[0].getAddress()); assertEquals("Max Mustermann", addresses[0].getPersonal()); @@ -27,7 +27,7 @@ public class Address extends TestCase { * test with multi email addresses */ public void testPraseWithValidEmailMulti() { - com.fsck.k9.mail.Address[] addresses = com.fsck.k9.mail.Address.parse("lorem@ipsum.us,mark@twain.com"); + Address[] addresses = Address.parse("lorem@ipsum.us,mark@twain.com"); assertEquals(2, addresses.length); assertEquals("lorem@ipsum.us", addresses[0].getAddress()); assertEquals(null, addresses[0].getPersonal()); diff --git a/tests/src/com/fsck/k9/helper/Utility_quoteAtoms.java b/tests/src/com/fsck/k9/mail/Address_quoteAtoms.java similarity index 87% rename from tests/src/com/fsck/k9/helper/Utility_quoteAtoms.java rename to tests/src/com/fsck/k9/mail/Address_quoteAtoms.java index 372c5c0f4..0ea553b2e 100644 --- a/tests/src/com/fsck/k9/helper/Utility_quoteAtoms.java +++ b/tests/src/com/fsck/k9/mail/Address_quoteAtoms.java @@ -1,8 +1,8 @@ -package com.fsck.k9.helper; +package com.fsck.k9.mail; import junit.framework.TestCase; -public class Utility_quoteAtoms extends TestCase +public class Address_quoteAtoms extends TestCase { public void testNoQuote() { // Alpha @@ -53,10 +53,10 @@ public class Utility_quoteAtoms extends TestCase } private void noQuote(final String s) { - assertEquals(s, Utility.quoteAtoms(s)); + assertEquals(s, Address.quoteAtoms(s)); } private String quote(final String s) { - return Utility.quoteAtoms(s); + return Address.quoteAtoms(s); } } diff --git a/tests/src/com/fsck/k9/mail/ssl/TrustManagerFactoryTest.java b/tests/src/com/fsck/k9/mail/ssl/TrustManagerFactoryTest.java index 14da17b7f..1574aaf03 100644 --- a/tests/src/com/fsck/k9/mail/ssl/TrustManagerFactoryTest.java +++ b/tests/src/com/fsck/k9/mail/ssl/TrustManagerFactoryTest.java @@ -2,9 +2,6 @@ package com.fsck.k9.mail.ssl; import javax.net.ssl.X509TrustManager; -import com.fsck.k9.mail.ssl.TrustManagerFactory; -import com.fsck.k9.security.LocalKeyStore; - import java.io.ByteArrayInputStream; import java.io.File; import java.security.cert.CertificateException; diff --git a/tests/src/com/fsck/k9/mail/store/imap/ImapUtilityTest.java b/tests/src/com/fsck/k9/mail/store/ImapUtilityTest.java similarity index 99% rename from tests/src/com/fsck/k9/mail/store/imap/ImapUtilityTest.java rename to tests/src/com/fsck/k9/mail/store/ImapUtilityTest.java index feaef05e8..392fdc554 100644 --- a/tests/src/com/fsck/k9/mail/store/imap/ImapUtilityTest.java +++ b/tests/src/com/fsck/k9/mail/store/ImapUtilityTest.java @@ -15,10 +15,11 @@ * limitations under the License. */ -package com.fsck.k9.mail.store.imap; +package com.fsck.k9.mail.store; import java.util.List; import android.test.MoreAsserts; + import junit.framework.TestCase; public class ImapUtilityTest extends TestCase { From b644194a3d2866ddc543a696b72ba5efa22432e5 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Fri, 12 Dec 2014 15:16:38 +0000 Subject: [PATCH 10/29] Fix LocalMessage equality/hash --- src/com/fsck/k9/local/LocalFolder.java | 2 -- src/com/fsck/k9/local/LocalMessage.java | 10 +++++++--- src/com/fsck/k9/mail/Message.java | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/com/fsck/k9/local/LocalFolder.java b/src/com/fsck/k9/local/LocalFolder.java index 33e177f9a..a35428adb 100644 --- a/src/com/fsck/k9/local/LocalFolder.java +++ b/src/com/fsck/k9/local/LocalFolder.java @@ -116,8 +116,6 @@ public class LocalFolder extends Folder implements Serializable { return getAccount().syncRemoteDeletions(); } - - @Override public void open(final int mode) throws MessagingException { diff --git a/src/com/fsck/k9/local/LocalMessage.java b/src/com/fsck/k9/local/LocalMessage.java index ac3aa5918..cc9cef2de 100644 --- a/src/com/fsck/k9/local/LocalMessage.java +++ b/src/com/fsck/k9/local/LocalMessage.java @@ -601,14 +601,18 @@ public class LocalMessage extends MimeMessage { if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; - LocalMessage that = (LocalMessage) o; - return !(getUid() != null ? !getUid().equals(that.getUid()) : that.getUid() != null); + final LocalMessage that = (LocalMessage) o; + return !(getAccountUuid() != null ? !getAccountUuid().equals(that.getAccountUuid()) : that.getAccountUuid() != null); } @Override public int hashCode() { int result = super.hashCode(); - result = 31 * result + (getUid() != null ? getUid().hashCode() : 0); + result = 31 * result + (getAccountUuid() != null ? getAccountUuid().hashCode() : 0); return result; } + + private String getAccountUuid() { + return getAccount().getUuid(); + } } \ No newline at end of file diff --git a/src/com/fsck/k9/mail/Message.java b/src/com/fsck/k9/mail/Message.java index b0bfe12af..24a29af5f 100644 --- a/src/com/fsck/k9/mail/Message.java +++ b/src/com/fsck/k9/mail/Message.java @@ -47,8 +47,8 @@ public abstract class Message implements Part, CompositeBody { return false; } Message other = (Message)o; - return (mUid.equals(other.getUid()) - && mFolder.getName().equals(other.getFolder().getName())); + return (getUid().equals(other.getUid()) + && getFolder().getName().equals(other.getFolder().getName())); } @Override From 54d62eb7b94e7df35a681efaf08ae424f8c320ff Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Fri, 12 Dec 2014 15:19:13 +0000 Subject: [PATCH 11/29] Naming --- src/com/fsck/k9/mail/transport/WebDavTransport.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/fsck/k9/mail/transport/WebDavTransport.java b/src/com/fsck/k9/mail/transport/WebDavTransport.java index 40b8b6884..839ec00d9 100644 --- a/src/com/fsck/k9/mail/transport/WebDavTransport.java +++ b/src/com/fsck/k9/mail/transport/WebDavTransport.java @@ -43,8 +43,8 @@ public class WebDavTransport extends Transport { private WebDavStore store; - public WebDavTransport(StoreConfig configInterface) throws MessagingException { - store = new WebDavStore(configInterface); + public WebDavTransport(StoreConfig storeConfig) throws MessagingException { + store = new WebDavStore(storeConfig); if (K9.DEBUG) Log.d(K9.LOG_TAG, ">>> New WebDavTransport creation complete"); From 12248bca9248bb469d2e24ae49eee697c87a16ef Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Fri, 12 Dec 2014 15:32:36 +0000 Subject: [PATCH 12/29] Fix test location --- .../fsck/k9/{helper => mail/internet}/HtmlConverterTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) rename tests/src/com/fsck/k9/{helper => mail/internet}/HtmlConverterTest.java (99%) diff --git a/tests/src/com/fsck/k9/helper/HtmlConverterTest.java b/tests/src/com/fsck/k9/mail/internet/HtmlConverterTest.java similarity index 99% rename from tests/src/com/fsck/k9/helper/HtmlConverterTest.java rename to tests/src/com/fsck/k9/mail/internet/HtmlConverterTest.java index 7ab254baf..7faea3b18 100644 --- a/tests/src/com/fsck/k9/helper/HtmlConverterTest.java +++ b/tests/src/com/fsck/k9/mail/internet/HtmlConverterTest.java @@ -1,6 +1,4 @@ -package com.fsck.k9.helper; - -import com.fsck.k9.mail.internet.HtmlConverter; +package com.fsck.k9.mail.internet; import junit.framework.TestCase; From bd697bb56d485802dba771ce1e18d09f8d352b3e Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 14 Dec 2014 15:26:38 +0000 Subject: [PATCH 13/29] getUuid() -> getAccountUuid() --- src/com/fsck/k9/controller/MessagingController.java | 2 +- src/com/fsck/k9/fragment/MessageListFragment.java | 4 ++-- src/com/fsck/k9/helper/MessageHelper.java | 2 +- src/com/fsck/k9/local/LocalFolder.java | 4 ++-- src/com/fsck/k9/local/LocalMessage.java | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index a955c9199..c95748874 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -329,7 +329,7 @@ public class MessagingController implements Runnable { long messageId = message.getId(); long folderId = message.getFolder().getId(); - EmailProviderCache cache = EmailProviderCache.getCache(message.getFolder().getUuid(), + EmailProviderCache cache = EmailProviderCache.getCache(message.getFolder().getAccountUuid(), mApplication.getApplicationContext()); return cache.isMessageHidden(messageId, folderId); } diff --git a/src/com/fsck/k9/fragment/MessageListFragment.java b/src/com/fsck/k9/fragment/MessageListFragment.java index dae7ec49d..46760b3fa 100644 --- a/src/com/fsck/k9/fragment/MessageListFragment.java +++ b/src/com/fsck/k9/fragment/MessageListFragment.java @@ -2434,7 +2434,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_MOVE, folder, - messages.get(0).getFolder().getUuid(), null, + messages.get(0).getFolder().getAccountUuid(), null, messages); } @@ -2463,7 +2463,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener } displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_COPY, folder, - messages.get(0).getFolder().getUuid(), + messages.get(0).getFolder().getAccountUuid(), null, messages); } diff --git a/src/com/fsck/k9/helper/MessageHelper.java b/src/com/fsck/k9/helper/MessageHelper.java index 4cde05621..5e88d3b5e 100644 --- a/src/com/fsck/k9/helper/MessageHelper.java +++ b/src/com/fsck/k9/helper/MessageHelper.java @@ -71,7 +71,7 @@ public class MessageHelper { } target.uid = message.getUid(); - target.account = message.getFolder().getUuid(); + target.account = message.getFolder().getAccountUuid(); target.uri = message.getUri(); } catch (MessagingException me) { Log.w(K9.LOG_TAG, "Unable to load message info", me); diff --git a/src/com/fsck/k9/local/LocalFolder.java b/src/com/fsck/k9/local/LocalFolder.java index a35428adb..9fa2bcc79 100644 --- a/src/com/fsck/k9/local/LocalFolder.java +++ b/src/com/fsck/k9/local/LocalFolder.java @@ -99,7 +99,7 @@ public class LocalFolder extends Folder implements Serializable { return mFolderId; } - public String getUuid() + public String getAccountUuid() { return getAccount().getUuid(); } @@ -1890,7 +1890,7 @@ public class LocalFolder extends Folder implements Serializable { public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { Cursor attachmentsCursor = null; try { - String accountUuid = getUuid(); + String accountUuid = getAccountUuid(); Context context = LocalFolder.this.localStore.mApplication; // Get attachment IDs diff --git a/src/com/fsck/k9/local/LocalMessage.java b/src/com/fsck/k9/local/LocalMessage.java index cc9cef2de..292be2c51 100644 --- a/src/com/fsck/k9/local/LocalMessage.java +++ b/src/com/fsck/k9/local/LocalMessage.java @@ -573,7 +573,7 @@ public class LocalMessage extends MimeMessage { mReference = new MessageReference(); mReference.folderName = getFolder().getName(); mReference.uid = mUid; - mReference.accountUuid = getFolder().getUuid(); + mReference.accountUuid = getFolder().getAccountUuid(); } return mReference; } From 8ef9eae0d64585b2ffa94b883760c2ff6375224c Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 14 Dec 2014 15:28:42 +0000 Subject: [PATCH 14/29] local -> mailstore --- src/com/fsck/k9/Account.java | 6 +++--- src/com/fsck/k9/K9.java | 2 +- src/com/fsck/k9/Preferences.java | 2 +- src/com/fsck/k9/activity/Accounts.java | 2 +- src/com/fsck/k9/activity/FolderList.java | 2 +- src/com/fsck/k9/activity/MessageCompose.java | 8 ++++---- src/com/fsck/k9/activity/MessageInfoHolder.java | 2 +- src/com/fsck/k9/activity/MessageList.java | 4 ++-- src/com/fsck/k9/activity/MessageReference.java | 4 ++-- src/com/fsck/k9/activity/UpgradeDatabases.java | 2 +- src/com/fsck/k9/activity/setup/AccountSettings.java | 4 ++-- src/com/fsck/k9/activity/setup/FolderSettings.java | 4 ++-- src/com/fsck/k9/cache/EmailProviderCache.java | 4 ++-- src/com/fsck/k9/controller/MessagingController.java | 12 ++++++------ .../controller/MessagingControllerPushReceiver.java | 4 ++-- src/com/fsck/k9/controller/MessagingListener.java | 2 +- src/com/fsck/k9/fragment/MessageListFragment.java | 6 +++--- src/com/fsck/k9/fragment/MessageViewFragment.java | 2 +- src/com/fsck/k9/helper/MessageHelper.java | 2 +- .../AttachmentMessageBodyUtil.java | 2 +- .../{local => mailstore}/BinaryAttachmentBody.java | 2 +- .../k9/{local => mailstore}/LocalAttachmentBody.java | 2 +- .../LocalAttachmentBodyPart.java | 2 +- .../LocalAttachmentMessageBody.java | 2 +- .../fsck/k9/{local => mailstore}/LocalFolder.java | 6 +++--- .../fsck/k9/{local => mailstore}/LocalMessage.java | 6 +++--- src/com/fsck/k9/{local => mailstore}/LocalStore.java | 8 ++++---- .../fsck/k9/{local => mailstore}/LocalTextBody.java | 2 +- .../k9/{local => mailstore}/LockableDatabase.java | 4 ++-- .../{local => mailstore}/MessageRemovalListener.java | 2 +- .../fsck/k9/{local => mailstore}/StorageManager.java | 2 +- .../{local => mailstore}/StoreSchemaDefinition.java | 2 +- .../fsck/k9/{local => mailstore}/TempFileBody.java | 2 +- .../k9/{local => mailstore}/TempFileMessageBody.java | 2 +- src/com/fsck/k9/{local => mailstore}/ThreadInfo.java | 2 +- .../UnavailableStorageException.java | 2 +- src/com/fsck/k9/preferences/AccountSettings.java | 2 +- src/com/fsck/k9/provider/AttachmentProvider.java | 6 +++--- src/com/fsck/k9/provider/EmailProvider.java | 10 +++++----- src/com/fsck/k9/provider/MessageProvider.java | 6 +++--- src/com/fsck/k9/search/SqlQueryBuilder.java | 4 ++-- src/com/fsck/k9/service/DatabaseUpgradeService.java | 2 +- .../fsck/k9/service/NotificationActionService.java | 2 +- src/com/fsck/k9/service/StorageGoneReceiver.java | 2 +- src/com/fsck/k9/service/StorageReceiver.java | 2 +- src/com/fsck/k9/view/AttachmentView.java | 2 +- src/com/fsck/k9/view/SingleMessageView.java | 4 ++-- 47 files changed, 83 insertions(+), 83 deletions(-) rename src/com/fsck/k9/{local => mailstore}/AttachmentMessageBodyUtil.java (97%) rename src/com/fsck/k9/{local => mailstore}/BinaryAttachmentBody.java (98%) rename src/com/fsck/k9/{local => mailstore}/LocalAttachmentBody.java (97%) rename src/com/fsck/k9/{local => mailstore}/LocalAttachmentBodyPart.java (96%) rename src/com/fsck/k9/{local => mailstore}/LocalAttachmentMessageBody.java (97%) rename src/com/fsck/k9/{local => mailstore}/LocalFolder.java (99%) rename src/com/fsck/k9/{local => mailstore}/LocalMessage.java (99%) rename src/com/fsck/k9/{local => mailstore}/LocalStore.java (99%) rename src/com/fsck/k9/{local => mailstore}/LocalTextBody.java (93%) rename src/com/fsck/k9/{local => mailstore}/LockableDatabase.java (99%) rename src/com/fsck/k9/{local => mailstore}/MessageRemovalListener.java (80%) rename src/com/fsck/k9/{local => mailstore}/StorageManager.java (99%) rename src/com/fsck/k9/{local => mailstore}/StoreSchemaDefinition.java (99%) rename src/com/fsck/k9/{local => mailstore}/TempFileBody.java (95%) rename src/com/fsck/k9/{local => mailstore}/TempFileMessageBody.java (97%) rename src/com/fsck/k9/{local => mailstore}/ThreadInfo.java (93%) rename src/com/fsck/k9/{local => mailstore}/UnavailableStorageException.java (96%) diff --git a/src/com/fsck/k9/Account.java b/src/com/fsck/k9/Account.java index 46f5c5d62..e86ee07a1 100644 --- a/src/com/fsck/k9/Account.java +++ b/src/com/fsck/k9/Account.java @@ -32,9 +32,9 @@ import com.fsck.k9.mail.Folder.FolderClass; import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.store.RemoteStore; import com.fsck.k9.mail.store.StoreConfig; -import com.fsck.k9.local.StorageManager; -import com.fsck.k9.local.StorageManager.StorageProvider; -import com.fsck.k9.local.LocalStore; +import com.fsck.k9.mailstore.StorageManager; +import com.fsck.k9.mailstore.StorageManager.StorageProvider; +import com.fsck.k9.mailstore.LocalStore; import com.fsck.k9.provider.EmailProvider; import com.fsck.k9.provider.EmailProvider.StatsColumns; import com.fsck.k9.search.ConditionsTreeNode; diff --git a/src/com/fsck/k9/K9.java b/src/com/fsck/k9/K9.java index 7e062d5f9..b565ffc8c 100644 --- a/src/com/fsck/k9/K9.java +++ b/src/com/fsck/k9/K9.java @@ -36,7 +36,7 @@ import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.internet.BinaryTempFileBody; -import com.fsck.k9.local.LocalStore; +import com.fsck.k9.mailstore.LocalStore; import com.fsck.k9.provider.UnreadWidgetProvider; import com.fsck.k9.mail.ssl.LocalKeyStore; import com.fsck.k9.service.BootReceiver; diff --git a/src/com/fsck/k9/Preferences.java b/src/com/fsck/k9/Preferences.java index a5c0cd4bc..016928eb0 100644 --- a/src/com/fsck/k9/Preferences.java +++ b/src/com/fsck/k9/Preferences.java @@ -13,7 +13,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.util.Log; -import com.fsck.k9.local.LocalStore; +import com.fsck.k9.mailstore.LocalStore; import com.fsck.k9.preferences.Editor; import com.fsck.k9.preferences.Storage; diff --git a/src/com/fsck/k9/activity/Accounts.java b/src/com/fsck/k9/activity/Accounts.java index c2a0ec21b..0a7041165 100644 --- a/src/com/fsck/k9/activity/Accounts.java +++ b/src/com/fsck/k9/activity/Accounts.java @@ -80,7 +80,7 @@ import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.store.RemoteStore; -import com.fsck.k9.local.StorageManager; +import com.fsck.k9.mailstore.StorageManager; import com.fsck.k9.mail.store.WebDavStore; import com.fsck.k9.preferences.SettingsExporter; import com.fsck.k9.preferences.SettingsImportExportException; diff --git a/src/com/fsck/k9/activity/FolderList.java b/src/com/fsck/k9/activity/FolderList.java index d1d7c17e4..254b87745 100644 --- a/src/com/fsck/k9/activity/FolderList.java +++ b/src/com/fsck/k9/activity/FolderList.java @@ -55,7 +55,7 @@ import com.fsck.k9.helper.power.TracingPowerManager; import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; -import com.fsck.k9.local.LocalFolder; +import com.fsck.k9.mailstore.LocalFolder; import com.fsck.k9.search.LocalSearch; import com.fsck.k9.search.SearchSpecification.Attribute; import com.fsck.k9.search.SearchSpecification.Searchfield; diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index d9f489052..30f3e11cb 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -109,10 +109,10 @@ import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mail.internet.TextBodyBuilder; -import com.fsck.k9.local.LocalAttachmentBody; -import com.fsck.k9.local.LocalMessage; -import com.fsck.k9.local.TempFileBody; -import com.fsck.k9.local.TempFileMessageBody; +import com.fsck.k9.mailstore.LocalAttachmentBody; +import com.fsck.k9.mailstore.LocalMessage; +import com.fsck.k9.mailstore.TempFileBody; +import com.fsck.k9.mailstore.TempFileMessageBody; import com.fsck.k9.view.MessageWebView; import org.apache.james.mime4j.codec.EncoderUtil; diff --git a/src/com/fsck/k9/activity/MessageInfoHolder.java b/src/com/fsck/k9/activity/MessageInfoHolder.java index 379543525..09708fbe6 100644 --- a/src/com/fsck/k9/activity/MessageInfoHolder.java +++ b/src/com/fsck/k9/activity/MessageInfoHolder.java @@ -2,7 +2,7 @@ package com.fsck.k9.activity; import java.util.Date; -import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.mailstore.LocalMessage; public class MessageInfoHolder { public String date; diff --git a/src/com/fsck/k9/activity/MessageList.java b/src/com/fsck/k9/activity/MessageList.java index ebfb3ef3b..569753f7b 100644 --- a/src/com/fsck/k9/activity/MessageList.java +++ b/src/com/fsck/k9/activity/MessageList.java @@ -42,8 +42,8 @@ import com.fsck.k9.fragment.MessageListFragment; import com.fsck.k9.fragment.MessageListFragment.MessageListFragmentListener; import com.fsck.k9.fragment.MessageViewFragment; import com.fsck.k9.fragment.MessageViewFragment.MessageViewFragmentListener; -import com.fsck.k9.local.StorageManager; -import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.mailstore.StorageManager; +import com.fsck.k9.mailstore.LocalMessage; import com.fsck.k9.search.LocalSearch; import com.fsck.k9.search.SearchAccount; import com.fsck.k9.search.SearchSpecification; diff --git a/src/com/fsck/k9/activity/MessageReference.java b/src/com/fsck/k9/activity/MessageReference.java index 0ba73a0c2..131b0715a 100644 --- a/src/com/fsck/k9/activity/MessageReference.java +++ b/src/com/fsck/k9/activity/MessageReference.java @@ -10,8 +10,8 @@ import com.fsck.k9.K9; import com.fsck.k9.Preferences; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.local.LocalFolder; -import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.mailstore.LocalFolder; +import com.fsck.k9.mailstore.LocalMessage; import com.fsck.k9.mail.filter.Base64; import java.util.StringTokenizer; diff --git a/src/com/fsck/k9/activity/UpgradeDatabases.java b/src/com/fsck/k9/activity/UpgradeDatabases.java index f5b2b9754..ae8cd6fd7 100644 --- a/src/com/fsck/k9/activity/UpgradeDatabases.java +++ b/src/com/fsck/k9/activity/UpgradeDatabases.java @@ -30,7 +30,7 @@ import android.widget.TextView; *
  • {@link #actionUpgradeDatabases(Context, Intent)} will call {@link K9#areDatabasesUpToDate()} * to check if we already know whether the databases have been upgraded.
  • *
  • {@link K9#areDatabasesUpToDate()} will compare the last known database version stored in a - * {@link SharedPreferences} file to {@link com.fsck.k9.local.LocalStore#DB_VERSION}. This + * {@link SharedPreferences} file to {@link com.fsck.k9.mailstore.LocalStore#DB_VERSION}. This * is done as an optimization because it's faster than opening all of the accounts' databases * one by one.
  • *
  • If there was an error reading the cached database version or if it shows the databases need diff --git a/src/com/fsck/k9/activity/setup/AccountSettings.java b/src/com/fsck/k9/activity/setup/AccountSettings.java index 2eac23514..c6ee715de 100644 --- a/src/com/fsck/k9/activity/setup/AccountSettings.java +++ b/src/com/fsck/k9/activity/setup/AccountSettings.java @@ -36,8 +36,8 @@ import com.fsck.k9.activity.K9PreferenceActivity; import com.fsck.k9.activity.ManageIdentities; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Store; -import com.fsck.k9.local.LocalFolder; -import com.fsck.k9.local.StorageManager; +import com.fsck.k9.mailstore.LocalFolder; +import com.fsck.k9.mailstore.StorageManager; import com.fsck.k9.service.MailService; import org.openintents.openpgp.util.OpenPgpListPreference; diff --git a/src/com/fsck/k9/activity/setup/FolderSettings.java b/src/com/fsck/k9/activity/setup/FolderSettings.java index af9ab8b67..737e2ffc3 100644 --- a/src/com/fsck/k9/activity/setup/FolderSettings.java +++ b/src/com/fsck/k9/activity/setup/FolderSettings.java @@ -16,8 +16,8 @@ import com.fsck.k9.mail.Folder.FolderClass; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Store; -import com.fsck.k9.local.LocalFolder; -import com.fsck.k9.local.LocalStore; +import com.fsck.k9.mailstore.LocalFolder; +import com.fsck.k9.mailstore.LocalStore; import com.fsck.k9.service.MailService; public class FolderSettings extends K9PreferenceActivity { diff --git a/src/com/fsck/k9/cache/EmailProviderCache.java b/src/com/fsck/k9/cache/EmailProviderCache.java index a37f66acd..81b785997 100644 --- a/src/com/fsck/k9/cache/EmailProviderCache.java +++ b/src/com/fsck/k9/cache/EmailProviderCache.java @@ -11,8 +11,8 @@ import android.support.v4.content.LocalBroadcastManager; import com.fsck.k9.fragment.MessageListFragment; import com.fsck.k9.mail.Message; -import com.fsck.k9.local.LocalFolder; -import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.mailstore.LocalFolder; +import com.fsck.k9.mailstore.LocalMessage; import com.fsck.k9.provider.EmailProvider; /** diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index c95748874..1e7e98885 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -81,14 +81,14 @@ import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; -import com.fsck.k9.local.MessageRemovalListener; +import com.fsck.k9.mailstore.MessageRemovalListener; import com.fsck.k9.mail.MessageRetrievalListener; -import com.fsck.k9.local.LocalFolder; -import com.fsck.k9.local.LocalMessage; -import com.fsck.k9.local.LocalStore; -import com.fsck.k9.local.LocalStore.PendingCommand; +import com.fsck.k9.mailstore.LocalFolder; +import com.fsck.k9.mailstore.LocalMessage; +import com.fsck.k9.mailstore.LocalStore; +import com.fsck.k9.mailstore.LocalStore.PendingCommand; import com.fsck.k9.mail.store.Pop3Store; -import com.fsck.k9.local.UnavailableStorageException; +import com.fsck.k9.mailstore.UnavailableStorageException; import com.fsck.k9.provider.EmailProvider; import com.fsck.k9.provider.EmailProvider.StatsColumns; import com.fsck.k9.search.ConditionsTreeNode; diff --git a/src/com/fsck/k9/controller/MessagingControllerPushReceiver.java b/src/com/fsck/k9/controller/MessagingControllerPushReceiver.java index d99f5feb1..3bdec8c22 100644 --- a/src/com/fsck/k9/controller/MessagingControllerPushReceiver.java +++ b/src/com/fsck/k9/controller/MessagingControllerPushReceiver.java @@ -11,8 +11,8 @@ import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.PushReceiver; -import com.fsck.k9.local.LocalFolder; -import com.fsck.k9.local.LocalStore; +import com.fsck.k9.mailstore.LocalFolder; +import com.fsck.k9.mailstore.LocalStore; import com.fsck.k9.service.SleepService; import java.util.List; diff --git a/src/com/fsck/k9/controller/MessagingListener.java b/src/com/fsck/k9/controller/MessagingListener.java index a0396b535..72bd93d18 100644 --- a/src/com/fsck/k9/controller/MessagingListener.java +++ b/src/com/fsck/k9/controller/MessagingListener.java @@ -11,7 +11,7 @@ import com.fsck.k9.BaseAccount; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Part; -import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.mailstore.LocalMessage; /** * Defines the interface that {@link MessagingController} will use to callback to requesters. diff --git a/src/com/fsck/k9/fragment/MessageListFragment.java b/src/com/fsck/k9/fragment/MessageListFragment.java index 46760b3fa..f697b6f2e 100644 --- a/src/com/fsck/k9/fragment/MessageListFragment.java +++ b/src/com/fsck/k9/fragment/MessageListFragment.java @@ -88,9 +88,9 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.local.LocalFolder; -import com.fsck.k9.local.LocalMessage; -import com.fsck.k9.local.LocalStore; +import com.fsck.k9.mailstore.LocalFolder; +import com.fsck.k9.mailstore.LocalMessage; +import com.fsck.k9.mailstore.LocalStore; import com.fsck.k9.provider.EmailProvider; import com.fsck.k9.provider.EmailProvider.MessageColumns; import com.fsck.k9.provider.EmailProvider.SpecialColumns; diff --git a/src/com/fsck/k9/fragment/MessageViewFragment.java b/src/com/fsck/k9/fragment/MessageViewFragment.java index 5ffee961a..961a38555 100644 --- a/src/com/fsck/k9/fragment/MessageViewFragment.java +++ b/src/com/fsck/k9/fragment/MessageViewFragment.java @@ -38,7 +38,7 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; -import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.mailstore.LocalMessage; import com.fsck.k9.view.AttachmentView; import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback; import com.fsck.k9.view.MessageHeader; diff --git a/src/com/fsck/k9/helper/MessageHelper.java b/src/com/fsck/k9/helper/MessageHelper.java index 5e88d3b5e..55caf8f66 100644 --- a/src/com/fsck/k9/helper/MessageHelper.java +++ b/src/com/fsck/k9/helper/MessageHelper.java @@ -13,7 +13,7 @@ import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Message.RecipientType; -import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.mailstore.LocalMessage; public class MessageHelper { diff --git a/src/com/fsck/k9/local/AttachmentMessageBodyUtil.java b/src/com/fsck/k9/mailstore/AttachmentMessageBodyUtil.java similarity index 97% rename from src/com/fsck/k9/local/AttachmentMessageBodyUtil.java rename to src/com/fsck/k9/mailstore/AttachmentMessageBodyUtil.java index 97a8a2394..71fc418d6 100644 --- a/src/com/fsck/k9/local/AttachmentMessageBodyUtil.java +++ b/src/com/fsck/k9/mailstore/AttachmentMessageBodyUtil.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import java.io.IOException; import java.io.InputStream; diff --git a/src/com/fsck/k9/local/BinaryAttachmentBody.java b/src/com/fsck/k9/mailstore/BinaryAttachmentBody.java similarity index 98% rename from src/com/fsck/k9/local/BinaryAttachmentBody.java rename to src/com/fsck/k9/mailstore/BinaryAttachmentBody.java index 466f6e57f..ecf40c75f 100644 --- a/src/com/fsck/k9/local/BinaryAttachmentBody.java +++ b/src/com/fsck/k9/mailstore/BinaryAttachmentBody.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import java.io.IOException; import java.io.InputStream; diff --git a/src/com/fsck/k9/local/LocalAttachmentBody.java b/src/com/fsck/k9/mailstore/LocalAttachmentBody.java similarity index 97% rename from src/com/fsck/k9/local/LocalAttachmentBody.java rename to src/com/fsck/k9/mailstore/LocalAttachmentBody.java index acfbe7980..0accef810 100644 --- a/src/com/fsck/k9/local/LocalAttachmentBody.java +++ b/src/com/fsck/k9/mailstore/LocalAttachmentBody.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; diff --git a/src/com/fsck/k9/local/LocalAttachmentBodyPart.java b/src/com/fsck/k9/mailstore/LocalAttachmentBodyPart.java similarity index 96% rename from src/com/fsck/k9/local/LocalAttachmentBodyPart.java rename to src/com/fsck/k9/mailstore/LocalAttachmentBodyPart.java index 7d5a19798..ab6eae968 100644 --- a/src/com/fsck/k9/local/LocalAttachmentBodyPart.java +++ b/src/com/fsck/k9/mailstore/LocalAttachmentBodyPart.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import com.fsck.k9.mail.Body; import com.fsck.k9.mail.MessagingException; diff --git a/src/com/fsck/k9/local/LocalAttachmentMessageBody.java b/src/com/fsck/k9/mailstore/LocalAttachmentMessageBody.java similarity index 97% rename from src/com/fsck/k9/local/LocalAttachmentMessageBody.java rename to src/com/fsck/k9/mailstore/LocalAttachmentMessageBody.java index 6dc632495..bd0a4329f 100644 --- a/src/com/fsck/k9/local/LocalAttachmentMessageBody.java +++ b/src/com/fsck/k9/mailstore/LocalAttachmentMessageBody.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import java.io.IOException; import java.io.OutputStream; diff --git a/src/com/fsck/k9/local/LocalFolder.java b/src/com/fsck/k9/mailstore/LocalFolder.java similarity index 99% rename from src/com/fsck/k9/local/LocalFolder.java rename to src/com/fsck/k9/mailstore/LocalFolder.java index 9fa2bcc79..edf81d931 100644 --- a/src/com/fsck/k9/local/LocalFolder.java +++ b/src/com/fsck/k9/mailstore/LocalFolder.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import java.io.File; import java.io.FileOutputStream; @@ -52,8 +52,8 @@ import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mail.internet.MimeUtility.ViewableContainer; -import com.fsck.k9.local.LockableDatabase.DbCallback; -import com.fsck.k9.local.LockableDatabase.WrappedException; +import com.fsck.k9.mailstore.LockableDatabase.DbCallback; +import com.fsck.k9.mailstore.LockableDatabase.WrappedException; import com.fsck.k9.provider.AttachmentProvider; public class LocalFolder extends Folder implements Serializable { diff --git a/src/com/fsck/k9/local/LocalMessage.java b/src/com/fsck/k9/mailstore/LocalMessage.java similarity index 99% rename from src/com/fsck/k9/local/LocalMessage.java rename to src/com/fsck/k9/mailstore/LocalMessage.java index 292be2c51..464a6b1e9 100644 --- a/src/com/fsck/k9/local/LocalMessage.java +++ b/src/com/fsck/k9/mailstore/LocalMessage.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import java.io.IOException; import java.io.OutputStream; @@ -22,8 +22,8 @@ import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeUtility; -import com.fsck.k9.local.LockableDatabase.DbCallback; -import com.fsck.k9.local.LockableDatabase.WrappedException; +import com.fsck.k9.mailstore.LockableDatabase.DbCallback; +import com.fsck.k9.mailstore.LockableDatabase.WrappedException; public class LocalMessage extends MimeMessage { protected MessageReference mReference; diff --git a/src/com/fsck/k9/local/LocalStore.java b/src/com/fsck/k9/mailstore/LocalStore.java similarity index 99% rename from src/com/fsck/k9/local/LocalStore.java rename to src/com/fsck/k9/mailstore/LocalStore.java index a7ec36f51..45ce9ad42 100644 --- a/src/com/fsck/k9/local/LocalStore.java +++ b/src/com/fsck/k9/mailstore/LocalStore.java @@ -1,5 +1,5 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import android.app.Application; import android.content.ContentResolver; @@ -22,10 +22,10 @@ import com.fsck.k9.mail.MessageRetrievalListener; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Store; import com.fsck.k9.mail.store.RemoteStore; -import com.fsck.k9.local.StorageManager.StorageProvider; +import com.fsck.k9.mailstore.StorageManager.StorageProvider; import com.fsck.k9.mail.store.StoreConfig; -import com.fsck.k9.local.LockableDatabase.DbCallback; -import com.fsck.k9.local.LockableDatabase.WrappedException; +import com.fsck.k9.mailstore.LockableDatabase.DbCallback; +import com.fsck.k9.mailstore.LockableDatabase.WrappedException; import com.fsck.k9.provider.EmailProvider; import com.fsck.k9.provider.EmailProvider.MessageColumns; import com.fsck.k9.search.LocalSearch; diff --git a/src/com/fsck/k9/local/LocalTextBody.java b/src/com/fsck/k9/mailstore/LocalTextBody.java similarity index 93% rename from src/com/fsck/k9/local/LocalTextBody.java rename to src/com/fsck/k9/mailstore/LocalTextBody.java index b40d79b8e..57c4963cf 100644 --- a/src/com/fsck/k9/local/LocalTextBody.java +++ b/src/com/fsck/k9/mailstore/LocalTextBody.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import com.fsck.k9.mail.internet.TextBody; diff --git a/src/com/fsck/k9/local/LockableDatabase.java b/src/com/fsck/k9/mailstore/LockableDatabase.java similarity index 99% rename from src/com/fsck/k9/local/LockableDatabase.java rename to src/com/fsck/k9/mailstore/LockableDatabase.java index 62f013953..f7c0a2258 100644 --- a/src/com/fsck/k9/local/LockableDatabase.java +++ b/src/com/fsck/k9/mailstore/LockableDatabase.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import java.io.File; import java.util.concurrent.locks.Lock; @@ -34,7 +34,7 @@ public class LockableDatabase { * @return Any relevant data. Can be null. * @throws WrappedException * @throws com.fsck.k9.mail.MessagingException - * @throws com.fsck.k9.local.UnavailableStorageException + * @throws com.fsck.k9.mailstore.UnavailableStorageException */ T doDbWork(SQLiteDatabase db) throws WrappedException, MessagingException; } diff --git a/src/com/fsck/k9/local/MessageRemovalListener.java b/src/com/fsck/k9/mailstore/MessageRemovalListener.java similarity index 80% rename from src/com/fsck/k9/local/MessageRemovalListener.java rename to src/com/fsck/k9/mailstore/MessageRemovalListener.java index 3f4d47d0e..51ad9a15e 100644 --- a/src/com/fsck/k9/local/MessageRemovalListener.java +++ b/src/com/fsck/k9/mailstore/MessageRemovalListener.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import com.fsck.k9.mail.Message; diff --git a/src/com/fsck/k9/local/StorageManager.java b/src/com/fsck/k9/mailstore/StorageManager.java similarity index 99% rename from src/com/fsck/k9/local/StorageManager.java rename to src/com/fsck/k9/mailstore/StorageManager.java index 20676d720..3aaf663dc 100644 --- a/src/com/fsck/k9/local/StorageManager.java +++ b/src/com/fsck/k9/mailstore/StorageManager.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import java.io.File; import java.io.IOException; diff --git a/src/com/fsck/k9/local/StoreSchemaDefinition.java b/src/com/fsck/k9/mailstore/StoreSchemaDefinition.java similarity index 99% rename from src/com/fsck/k9/local/StoreSchemaDefinition.java rename to src/com/fsck/k9/mailstore/StoreSchemaDefinition.java index c893e5888..f9e786dba 100644 --- a/src/com/fsck/k9/local/StoreSchemaDefinition.java +++ b/src/com/fsck/k9/mailstore/StoreSchemaDefinition.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import java.util.ArrayList; import java.util.List; diff --git a/src/com/fsck/k9/local/TempFileBody.java b/src/com/fsck/k9/mailstore/TempFileBody.java similarity index 95% rename from src/com/fsck/k9/local/TempFileBody.java rename to src/com/fsck/k9/mailstore/TempFileBody.java index c687d6082..934b3bd68 100644 --- a/src/com/fsck/k9/local/TempFileBody.java +++ b/src/com/fsck/k9/mailstore/TempFileBody.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import java.io.ByteArrayInputStream; import java.io.File; diff --git a/src/com/fsck/k9/local/TempFileMessageBody.java b/src/com/fsck/k9/mailstore/TempFileMessageBody.java similarity index 97% rename from src/com/fsck/k9/local/TempFileMessageBody.java rename to src/com/fsck/k9/mailstore/TempFileMessageBody.java index 55f6b76e4..c310ae2e4 100644 --- a/src/com/fsck/k9/local/TempFileMessageBody.java +++ b/src/com/fsck/k9/mailstore/TempFileMessageBody.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import java.io.IOException; import java.io.OutputStream; diff --git a/src/com/fsck/k9/local/ThreadInfo.java b/src/com/fsck/k9/mailstore/ThreadInfo.java similarity index 93% rename from src/com/fsck/k9/local/ThreadInfo.java rename to src/com/fsck/k9/mailstore/ThreadInfo.java index cf72dddc1..9f9ee2204 100644 --- a/src/com/fsck/k9/local/ThreadInfo.java +++ b/src/com/fsck/k9/mailstore/ThreadInfo.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; class ThreadInfo { public final long threadId; diff --git a/src/com/fsck/k9/local/UnavailableStorageException.java b/src/com/fsck/k9/mailstore/UnavailableStorageException.java similarity index 96% rename from src/com/fsck/k9/local/UnavailableStorageException.java rename to src/com/fsck/k9/mailstore/UnavailableStorageException.java index d6e1cd69d..d764a0e39 100644 --- a/src/com/fsck/k9/local/UnavailableStorageException.java +++ b/src/com/fsck/k9/mailstore/UnavailableStorageException.java @@ -1,4 +1,4 @@ -package com.fsck.k9.local; +package com.fsck.k9.mailstore; import com.fsck.k9.mail.MessagingException; diff --git a/src/com/fsck/k9/preferences/AccountSettings.java b/src/com/fsck/k9/preferences/AccountSettings.java index 327ff38b2..6bdcb4699 100644 --- a/src/com/fsck/k9/preferences/AccountSettings.java +++ b/src/com/fsck/k9/preferences/AccountSettings.java @@ -13,7 +13,7 @@ import com.fsck.k9.Account.SortType; import com.fsck.k9.K9; import com.fsck.k9.R; import com.fsck.k9.Account.FolderMode; -import com.fsck.k9.local.StorageManager; +import com.fsck.k9.mailstore.StorageManager; import com.fsck.k9.preferences.Settings.*; public class AccountSettings { diff --git a/src/com/fsck/k9/provider/AttachmentProvider.java b/src/com/fsck/k9/provider/AttachmentProvider.java index 792c8885f..6516f60ef 100644 --- a/src/com/fsck/k9/provider/AttachmentProvider.java +++ b/src/com/fsck/k9/provider/AttachmentProvider.java @@ -15,9 +15,9 @@ import com.fsck.k9.K9; import com.fsck.k9.Preferences; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.internet.MimeUtility; -import com.fsck.k9.local.LocalStore; -import com.fsck.k9.local.LocalStore.AttachmentInfo; -import com.fsck.k9.local.StorageManager; +import com.fsck.k9.mailstore.LocalStore; +import com.fsck.k9.mailstore.LocalStore.AttachmentInfo; +import com.fsck.k9.mailstore.StorageManager; import java.io.*; import java.util.List; diff --git a/src/com/fsck/k9/provider/EmailProvider.java b/src/com/fsck/k9/provider/EmailProvider.java index fb025f46c..d599f4ac7 100644 --- a/src/com/fsck/k9/provider/EmailProvider.java +++ b/src/com/fsck/k9/provider/EmailProvider.java @@ -10,11 +10,11 @@ import com.fsck.k9.Preferences; import com.fsck.k9.cache.EmailProviderCacheCursor; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.local.LockableDatabase; -import com.fsck.k9.local.LockableDatabase.DbCallback; -import com.fsck.k9.local.LockableDatabase.WrappedException; -import com.fsck.k9.local.UnavailableStorageException; -import com.fsck.k9.local.LocalStore; +import com.fsck.k9.mailstore.LockableDatabase; +import com.fsck.k9.mailstore.LockableDatabase.DbCallback; +import com.fsck.k9.mailstore.LockableDatabase.WrappedException; +import com.fsck.k9.mailstore.UnavailableStorageException; +import com.fsck.k9.mailstore.LocalStore; import com.fsck.k9.search.SqlQueryBuilder; import android.content.ContentProvider; diff --git a/src/com/fsck/k9/provider/MessageProvider.java b/src/com/fsck/k9/provider/MessageProvider.java index a02bf5520..4508edf1b 100644 --- a/src/com/fsck/k9/provider/MessageProvider.java +++ b/src/com/fsck/k9/provider/MessageProvider.java @@ -32,9 +32,9 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.local.LocalFolder; -import com.fsck.k9.local.LocalMessage; -import com.fsck.k9.local.LocalStore; +import com.fsck.k9.mailstore.LocalFolder; +import com.fsck.k9.mailstore.LocalMessage; +import com.fsck.k9.mailstore.LocalStore; import com.fsck.k9.search.SearchAccount; import java.lang.ref.WeakReference; diff --git a/src/com/fsck/k9/search/SqlQueryBuilder.java b/src/com/fsck/k9/search/SqlQueryBuilder.java index 8286f5d9e..118467301 100644 --- a/src/com/fsck/k9/search/SqlQueryBuilder.java +++ b/src/com/fsck/k9/search/SqlQueryBuilder.java @@ -5,8 +5,8 @@ import java.util.List; import com.fsck.k9.Account; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Folder; -import com.fsck.k9.local.LocalFolder; -import com.fsck.k9.local.LocalStore; +import com.fsck.k9.mailstore.LocalFolder; +import com.fsck.k9.mailstore.LocalStore; import com.fsck.k9.search.SearchSpecification.Attribute; import com.fsck.k9.search.SearchSpecification.SearchCondition; import com.fsck.k9.search.SearchSpecification.Searchfield; diff --git a/src/com/fsck/k9/service/DatabaseUpgradeService.java b/src/com/fsck/k9/service/DatabaseUpgradeService.java index 15f0e16f1..d0fe0b5b4 100644 --- a/src/com/fsck/k9/service/DatabaseUpgradeService.java +++ b/src/com/fsck/k9/service/DatabaseUpgradeService.java @@ -17,7 +17,7 @@ import com.fsck.k9.Preferences; import com.fsck.k9.activity.UpgradeDatabases; import com.fsck.k9.helper.power.TracingPowerManager; import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock; -import com.fsck.k9.local.UnavailableStorageException; +import com.fsck.k9.mailstore.UnavailableStorageException; /** * Service used to upgrade the accounts' databases and/or track the progress of the upgrade. diff --git a/src/com/fsck/k9/service/NotificationActionService.java b/src/com/fsck/k9/service/NotificationActionService.java index 96d3cf705..50f14bc15 100644 --- a/src/com/fsck/k9/service/NotificationActionService.java +++ b/src/com/fsck/k9/service/NotificationActionService.java @@ -11,7 +11,7 @@ import com.fsck.k9.activity.MessageCompose; import com.fsck.k9.activity.MessageReference; import com.fsck.k9.controller.MessagingController; import com.fsck.k9.mail.Flag; -import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.mailstore.LocalMessage; import android.app.PendingIntent; import android.content.Context; diff --git a/src/com/fsck/k9/service/StorageGoneReceiver.java b/src/com/fsck/k9/service/StorageGoneReceiver.java index 022ae9b9c..592656f9d 100644 --- a/src/com/fsck/k9/service/StorageGoneReceiver.java +++ b/src/com/fsck/k9/service/StorageGoneReceiver.java @@ -7,7 +7,7 @@ import android.net.Uri; import android.util.Log; import com.fsck.k9.K9; -import com.fsck.k9.local.StorageManager; +import com.fsck.k9.mailstore.StorageManager; /** * That BroadcastReceiver is only interested in UNMOUNT events. diff --git a/src/com/fsck/k9/service/StorageReceiver.java b/src/com/fsck/k9/service/StorageReceiver.java index 1cf116dd6..abf36dd13 100644 --- a/src/com/fsck/k9/service/StorageReceiver.java +++ b/src/com/fsck/k9/service/StorageReceiver.java @@ -7,7 +7,7 @@ import android.net.Uri; import android.util.Log; import com.fsck.k9.K9; -import com.fsck.k9.local.StorageManager; +import com.fsck.k9.mailstore.StorageManager; /** * That BroadcastReceiver is only interested in MOUNT events. diff --git a/src/com/fsck/k9/view/AttachmentView.java b/src/com/fsck/k9/view/AttachmentView.java index 84d0d9ff8..7552c23b5 100644 --- a/src/com/fsck/k9/view/AttachmentView.java +++ b/src/com/fsck/k9/view/AttachmentView.java @@ -43,7 +43,7 @@ import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MimeHeader; import com.fsck.k9.mail.internet.MimeUtility; -import com.fsck.k9.local.LocalAttachmentBodyPart; +import com.fsck.k9.mailstore.LocalAttachmentBodyPart; import com.fsck.k9.provider.AttachmentProvider; import org.apache.commons.io.IOUtils; diff --git a/src/com/fsck/k9/view/SingleMessageView.java b/src/com/fsck/k9/view/SingleMessageView.java index 4747415ef..3ebdd89a3 100644 --- a/src/com/fsck/k9/view/SingleMessageView.java +++ b/src/com/fsck/k9/view/SingleMessageView.java @@ -56,8 +56,8 @@ import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Multipart; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MimeUtility; -import com.fsck.k9.local.LocalAttachmentBodyPart; -import com.fsck.k9.local.LocalMessage; +import com.fsck.k9.mailstore.LocalAttachmentBodyPart; +import com.fsck.k9.mailstore.LocalMessage; import com.fsck.k9.provider.AttachmentProvider.AttachmentProviderColumns; import org.apache.commons.io.IOUtils; From 238c1650c53bb3b99d3a2c19e4ced01a3511258f Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 14 Dec 2014 15:54:27 +0000 Subject: [PATCH 15/29] Remove URLEncodingHelper dependency --- src/com/fsck/k9/mail/Store.java | 19 ++++++++++++++ src/com/fsck/k9/mail/Transport.java | 19 ++++++++++++++ src/com/fsck/k9/mail/store/ImapStore.java | 19 +++++++------- src/com/fsck/k9/mail/store/Pop3Store.java | 13 +++++----- src/com/fsck/k9/mail/store/WebDavStore.java | 25 +++++++++---------- .../fsck/k9/mail/transport/SmtpTransport.java | 19 +++++++------- 6 files changed, 74 insertions(+), 40 deletions(-) diff --git a/src/com/fsck/k9/mail/Store.java b/src/com/fsck/k9/mail/Store.java index 424b6bca1..0892279c5 100644 --- a/src/com/fsck/k9/mail/Store.java +++ b/src/com/fsck/k9/mail/Store.java @@ -1,6 +1,9 @@ package com.fsck.k9.mail; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; import java.util.List; /** @@ -47,4 +50,20 @@ public abstract class Store { public Pusher getPusher(PushReceiver receiver) { return null; } + + protected static String decodeUtf8(String s) { + try { + return URLDecoder.decode(s, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not found"); + } + } + + protected static String encodeUtf8(String s) { + try { + return URLEncoder.encode(s, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not found"); + } + } } diff --git a/src/com/fsck/k9/mail/Transport.java b/src/com/fsck/k9/mail/Transport.java index 97e4ada1d..17d3e6866 100644 --- a/src/com/fsck/k9/mail/Transport.java +++ b/src/com/fsck/k9/mail/Transport.java @@ -5,6 +5,10 @@ import com.fsck.k9.mail.store.StoreConfig; import com.fsck.k9.mail.transport.SmtpTransport; import com.fsck.k9.mail.transport.WebDavTransport; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; + public abstract class Transport { protected static final int SOCKET_CONNECT_TIMEOUT = 10000; @@ -71,4 +75,19 @@ public abstract class Transport { public abstract void sendMessage(Message message) throws MessagingException; public abstract void close(); + + protected static String encodeUtf8(String s) { + try { + return URLEncoder.encode(s, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not found"); + } + } + protected static String decodeUtf8(String s) { + try { + return URLDecoder.decode(s, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not found"); + } + } } diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java index 051c29f2d..c5eb8ead3 100644 --- a/src/com/fsck/k9/mail/store/ImapStore.java +++ b/src/com/fsck/k9/mail/store/ImapStore.java @@ -57,7 +57,6 @@ import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.R; -import com.fsck.k9.helper.UrlEncodingHelper; import com.fsck.k9.helper.power.TracingPowerManager; import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock; import com.fsck.k9.mail.AuthType; @@ -197,19 +196,19 @@ public class ImapStore extends RemoteStore { if (userinfo.endsWith(":")) { // Password is empty. This can only happen after an account was imported. authenticationType = AuthType.valueOf(userInfoParts[0]); - username = UrlEncodingHelper.decodeUtf8(userInfoParts[1]); + username = decodeUtf8(userInfoParts[1]); } else if (userInfoParts.length == 2) { authenticationType = AuthType.PLAIN; - username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]); - password = UrlEncodingHelper.decodeUtf8(userInfoParts[1]); + username = decodeUtf8(userInfoParts[0]); + password = decodeUtf8(userInfoParts[1]); } else if (userInfoParts.length == 3) { authenticationType = AuthType.valueOf(userInfoParts[0]); - username = UrlEncodingHelper.decodeUtf8(userInfoParts[1]); + username = decodeUtf8(userInfoParts[1]); if (AuthType.EXTERNAL == authenticationType) { - clientCertificateAlias = UrlEncodingHelper.decodeUtf8(userInfoParts[2]); + clientCertificateAlias = decodeUtf8(userInfoParts[2]); } else { - password = UrlEncodingHelper.decodeUtf8(userInfoParts[2]); + password = decodeUtf8(userInfoParts[2]); } } } @@ -248,11 +247,11 @@ public class ImapStore extends RemoteStore { * @see ImapStore#decodeUri(String) */ public static String createUri(ServerSettings server) { - String userEnc = UrlEncodingHelper.encodeUtf8(server.username); + String userEnc = encodeUtf8(server.username); String passwordEnc = (server.password != null) ? - UrlEncodingHelper.encodeUtf8(server.password) : ""; + encodeUtf8(server.password) : ""; String clientCertificateAliasEnc = (server.clientCertificateAlias != null) ? - UrlEncodingHelper.encodeUtf8(server.clientCertificateAlias) : ""; + encodeUtf8(server.clientCertificateAlias) : ""; String scheme; switch (server.connectionSecurity) { diff --git a/src/com/fsck/k9/mail/store/Pop3Store.java b/src/com/fsck/k9/mail/store/Pop3Store.java index 85c73f0f0..b2d687fce 100644 --- a/src/com/fsck/k9/mail/store/Pop3Store.java +++ b/src/com/fsck/k9/mail/store/Pop3Store.java @@ -5,7 +5,6 @@ import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.R; -import com.fsck.k9.helper.UrlEncodingHelper; import com.fsck.k9.mail.*; import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.filter.Hex; @@ -125,12 +124,12 @@ public class Pop3Store extends RemoteStore { passwordIndex++; authType = AuthType.valueOf(userInfoParts[0]); } - username = UrlEncodingHelper.decodeUtf8(userInfoParts[userIndex]); + username = decodeUtf8(userInfoParts[userIndex]); if (userInfoParts.length > passwordIndex) { if (authType == AuthType.EXTERNAL) { - clientCertificateAlias = UrlEncodingHelper.decodeUtf8(userInfoParts[passwordIndex]); + clientCertificateAlias = decodeUtf8(userInfoParts[passwordIndex]); } else { - password = UrlEncodingHelper.decodeUtf8(userInfoParts[passwordIndex]); + password = decodeUtf8(userInfoParts[passwordIndex]); } } } @@ -151,11 +150,11 @@ public class Pop3Store extends RemoteStore { * @see Pop3Store#decodeUri(String) */ public static String createUri(ServerSettings server) { - String userEnc = UrlEncodingHelper.encodeUtf8(server.username); + String userEnc = encodeUtf8(server.username); String passwordEnc = (server.password != null) ? - UrlEncodingHelper.encodeUtf8(server.password) : ""; + encodeUtf8(server.password) : ""; String clientCertificateAliasEnc = (server.clientCertificateAlias != null) ? - UrlEncodingHelper.encodeUtf8(server.clientCertificateAlias) : ""; + encodeUtf8(server.clientCertificateAlias) : ""; String scheme; switch (server.connectionSecurity) { diff --git a/src/com/fsck/k9/mail/store/WebDavStore.java b/src/com/fsck/k9/mail/store/WebDavStore.java index a08d11cbe..f1202ff45 100644 --- a/src/com/fsck/k9/mail/store/WebDavStore.java +++ b/src/com/fsck/k9/mail/store/WebDavStore.java @@ -4,7 +4,6 @@ import android.util.Log; import com.fsck.k9.K9; -import com.fsck.k9.helper.UrlEncodingHelper; import com.fsck.k9.mail.*; import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.filter.EOLConvertingOutputStream; @@ -137,7 +136,7 @@ public class WebDavStore extends RemoteStore { String userInfo = webDavUri.getUserInfo(); if (userInfo != null) { String[] userInfoParts = userInfo.split(":"); - username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]); + username = decodeUtf8(userInfoParts[0]); String userParts[] = username.split("\\\\", 2); if (userParts.length > 1) { @@ -146,7 +145,7 @@ public class WebDavStore extends RemoteStore { alias = username; } if (userInfoParts.length > 1) { - password = UrlEncodingHelper.decodeUtf8(userInfoParts[1]); + password = decodeUtf8(userInfoParts[1]); } } @@ -186,9 +185,9 @@ public class WebDavStore extends RemoteStore { * @see WebDavStore#decodeUri(String) */ public static String createUri(ServerSettings server) { - String userEnc = UrlEncodingHelper.encodeUtf8(server.username); + String userEnc = encodeUtf8(server.username); String passwordEnc = (server.password != null) ? - UrlEncodingHelper.encodeUtf8(server.password) : ""; + encodeUtf8(server.password) : ""; String scheme; switch (server.connectionSecurity) { @@ -474,7 +473,7 @@ public class WebDavStore extends RemoteStore { // Decodes the url-encoded folder name (i.e. "My%20folder" => "My Folder" - return UrlEncodingHelper.decodeUtf8(fullPathName); + return decodeUtf8(fullPathName); } return null; @@ -1237,9 +1236,9 @@ public class WebDavStore extends RemoteStore { String url = ""; for (int i = 0, count = urlParts.length; i < count; i++) { if (i != 0) { - url = url + "/" + UrlEncodingHelper.encodeUtf8(urlParts[i]); + url = url + "/" + encodeUtf8(urlParts[i]); } else { - url = UrlEncodingHelper.encodeUtf8(urlParts[i]); + url = encodeUtf8(urlParts[i]); } } encodedName = url; @@ -1880,7 +1879,7 @@ public class WebDavStore extends RemoteStore { if (!messageURL.endsWith("/")) { messageURL += "/"; } - messageURL += UrlEncodingHelper.encodeUtf8(message.getUid() + ":" + System.currentTimeMillis() + ".eml"); + messageURL += encodeUtf8(message.getUid() + ":" + System.currentTimeMillis() + ".eml"); Log.i(K9.LOG_TAG, "Uploading message as " + messageURL); @@ -1967,8 +1966,8 @@ public class WebDavStore extends RemoteStore { * We have to decode, then encode the URL because Exchange likes to not properly encode all characters */ try { - end = UrlEncodingHelper.decodeUtf8(end); - end = UrlEncodingHelper.encodeUtf8(end); + end = decodeUtf8(end); + end = encodeUtf8(end); end = end.replaceAll("\\+", "%20"); } catch (IllegalArgumentException iae) { Log.e(K9.LOG_TAG, "IllegalArgumentException caught in setUrl: " + iae + "\nTrace: " @@ -2372,8 +2371,8 @@ public class WebDavStore extends RemoteStore { */ try { if (length > 3) { - end = UrlEncodingHelper.decodeUtf8(end); - end = UrlEncodingHelper.encodeUtf8(end); + end = decodeUtf8(end); + end = encodeUtf8(end); end = end.replaceAll("\\+", "%20"); } } catch (IllegalArgumentException iae) { diff --git a/src/com/fsck/k9/mail/transport/SmtpTransport.java b/src/com/fsck/k9/mail/transport/SmtpTransport.java index 6e30eab20..568f30b5e 100644 --- a/src/com/fsck/k9/mail/transport/SmtpTransport.java +++ b/src/com/fsck/k9/mail/transport/SmtpTransport.java @@ -5,7 +5,6 @@ import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.R; -import com.fsck.k9.helper.UrlEncodingHelper; import com.fsck.k9.mail.*; import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.filter.Base64; @@ -94,19 +93,19 @@ public class SmtpTransport extends Transport { String[] userInfoParts = smtpUri.getUserInfo().split(":"); if (userInfoParts.length == 1) { authType = AuthType.PLAIN; - username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]); + username = decodeUtf8(userInfoParts[0]); } else if (userInfoParts.length == 2) { authType = AuthType.PLAIN; - username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]); - password = UrlEncodingHelper.decodeUtf8(userInfoParts[1]); + username = decodeUtf8(userInfoParts[0]); + password = decodeUtf8(userInfoParts[1]); } else if (userInfoParts.length == 3) { // NOTE: In SmptTransport URIs, the authType comes last! authType = AuthType.valueOf(userInfoParts[2]); - username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]); + username = decodeUtf8(userInfoParts[0]); if (authType == AuthType.EXTERNAL) { - clientCertificateAlias = UrlEncodingHelper.decodeUtf8(userInfoParts[1]); + clientCertificateAlias = decodeUtf8(userInfoParts[1]); } else { - password = UrlEncodingHelper.decodeUtf8(userInfoParts[1]); + password = decodeUtf8(userInfoParts[1]); } } } @@ -128,11 +127,11 @@ public class SmtpTransport extends Transport { */ public static String createUri(ServerSettings server) { String userEnc = (server.username != null) ? - UrlEncodingHelper.encodeUtf8(server.username) : ""; + encodeUtf8(server.username) : ""; String passwordEnc = (server.password != null) ? - UrlEncodingHelper.encodeUtf8(server.password) : ""; + encodeUtf8(server.password) : ""; String clientCertificateAliasEnc = (server.clientCertificateAlias != null) ? - UrlEncodingHelper.encodeUtf8(server.clientCertificateAlias) : ""; + encodeUtf8(server.clientCertificateAlias) : ""; String scheme; switch (server.connectionSecurity) { From 476cb1d4ce6a2f1236b8b193362ac604e33bb13a Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 14 Dec 2014 15:58:08 +0000 Subject: [PATCH 16/29] Tidy responsibilities --- src/com/fsck/k9/Preferences.java | 6 ++++++ src/com/fsck/k9/mail/store/RemoteStore.java | 2 -- src/com/fsck/k9/mailstore/LocalStore.java | 7 ------- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/com/fsck/k9/Preferences.java b/src/com/fsck/k9/Preferences.java index 016928eb0..03a3cd1be 100644 --- a/src/com/fsck/k9/Preferences.java +++ b/src/com/fsck/k9/Preferences.java @@ -13,6 +13,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.util.Log; +import com.fsck.k9.mail.store.RemoteStore; import com.fsck.k9.mailstore.LocalStore; import com.fsck.k9.preferences.Editor; import com.fsck.k9.preferences.Storage; @@ -121,6 +122,11 @@ public class Preferences { accountsInOrder.remove(account); } + try { + RemoteStore.removeInstance(account); + } catch (Exception e) { + Log.e(K9.LOG_TAG, "Failed to reset remote store for account " + account.getUuid(), e); + } LocalStore.removeAccount(account); account.deleteCertificates(); diff --git a/src/com/fsck/k9/mail/store/RemoteStore.java b/src/com/fsck/k9/mail/store/RemoteStore.java index 9cad2aac2..a1f32722e 100644 --- a/src/com/fsck/k9/mail/store/RemoteStore.java +++ b/src/com/fsck/k9/mail/store/RemoteStore.java @@ -62,12 +62,10 @@ public abstract class RemoteStore extends Store { */ public static void removeInstance(StoreConfig storeConfig) { String uri = storeConfig.getStoreUri(); - if (uri.startsWith("local")) { throw new RuntimeException("Asked to get non-local Store object but given " + "LocalStore URI"); } - sStores.remove(uri); } diff --git a/src/com/fsck/k9/mailstore/LocalStore.java b/src/com/fsck/k9/mailstore/LocalStore.java index 45ce9ad42..b05a9efa2 100644 --- a/src/com/fsck/k9/mailstore/LocalStore.java +++ b/src/com/fsck/k9/mailstore/LocalStore.java @@ -21,7 +21,6 @@ import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.MessageRetrievalListener; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Store; -import com.fsck.k9.mail.store.RemoteStore; import com.fsck.k9.mailstore.StorageManager.StorageProvider; import com.fsck.k9.mail.store.StoreConfig; import com.fsck.k9.mailstore.LockableDatabase.DbCallback; @@ -207,12 +206,6 @@ public class LocalStore extends Store implements Serializable { } public static void removeAccount(Account account) { - try { - RemoteStore.removeInstance(account); - } catch (Exception e) { - Log.e(K9.LOG_TAG, "Failed to reset remote store for account " + account.getUuid(), e); - } - try { removeInstance(account); } catch (Exception e) { From 7d6e6b8abea0e9072de911ee061e78d86c6e66f3 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Mon, 15 Dec 2014 12:05:21 +0100 Subject: [PATCH 17/29] MimeUtility / Message refactor * break MimeUtility class into manageable pieces (MessageExtractor/CharsetSupport) * move HTML related code out of the mail package --- .../InsertableHtmlContent.java | 4 +- src/com/fsck/k9/activity/MessageCompose.java | 30 +- .../TextBodyBuilder.java | 6 +- .../k9/activity/setup/WelcomeMessage.java | 2 +- .../k9/controller/MessagingController.java | 11 +- src/com/fsck/k9/crypto/CryptoHelper.java | 12 +- .../internet => helper}/HtmlConverter.java | 2 +- src/com/fsck/k9/mail/BodyPart.java | 23 +- src/com/fsck/k9/mail/Message.java | 69 + src/com/fsck/k9/mail/Multipart.java | 4 +- src/com/fsck/k9/mail/Part.java | 38 +- .../fsck/k9/mail/internet/CharsetSupport.java | 1121 ++++++++ .../fsck/k9/mail/internet/DecoderUtil.java | 6 +- .../fsck/k9/mail/internet/EncoderUtil.java | 2 +- src/com/fsck/k9/mail/internet/JisSupport.java | 103 + .../k9/mail/internet/MessageExtractor.java | 410 +++ .../fsck/k9/mail/internet/MimeMessage.java | 2 +- .../fsck/k9/mail/internet/MimeUtility.java | 2353 +---------------- src/com/fsck/k9/mail/internet/TextBody.java | 6 +- src/com/fsck/k9/mail/internet/Viewable.java | 105 + .../fsck/k9/mail/transport/SmtpTransport.java | 4 +- src/com/fsck/k9/mailstore/LocalFolder.java | 10 +- src/com/fsck/k9/mailstore/LocalMessage.java | 6 +- .../k9/mailstore/LocalMessageExtractor.java | 464 ++++ .../fsck/k9/mailstore/ViewableContainer.java | 34 + src/com/fsck/k9/view/MessageOpenPgpView.java | 9 +- src/com/fsck/k9/view/MessageWebView.java | 2 +- src/com/fsck/k9/view/SingleMessageView.java | 2 +- .../TextBodyBuilderTest.java | 3 +- .../k9/mail/internet/CharsetSupportTest.java | 94 + .../k9/mail/internet/MimeUtilityTest.java | 98 - .../HtmlConverterTest.java | 4 +- tests/src/com/fsck/k9/mail/MessageTest.java | 4 +- .../LocalMessageExtractorTest.java} | 19 +- 34 files changed, 2549 insertions(+), 2513 deletions(-) rename src/com/fsck/k9/{mail/internet => activity}/InsertableHtmlContent.java (98%) rename src/com/fsck/k9/{mail/internet => activity}/TextBodyBuilder.java (98%) rename src/com/fsck/k9/{mail/internet => helper}/HtmlConverter.java (99%) create mode 100644 src/com/fsck/k9/mail/internet/CharsetSupport.java create mode 100644 src/com/fsck/k9/mail/internet/JisSupport.java create mode 100644 src/com/fsck/k9/mail/internet/MessageExtractor.java create mode 100644 src/com/fsck/k9/mail/internet/Viewable.java create mode 100644 src/com/fsck/k9/mailstore/LocalMessageExtractor.java create mode 100644 src/com/fsck/k9/mailstore/ViewableContainer.java rename tests-on-jvm/src/com/fsck/k9/{mail/internet => activity}/TextBodyBuilderTest.java (99%) create mode 100644 tests-on-jvm/src/com/fsck/k9/mail/internet/CharsetSupportTest.java rename tests/src/com/fsck/k9/{mail/internet => helper}/HtmlConverterTest.java (99%) rename tests/src/com/fsck/k9/{mail/internet/ViewablesTest.java => mailstore/LocalMessageExtractorTest.java} (89%) diff --git a/src/com/fsck/k9/mail/internet/InsertableHtmlContent.java b/src/com/fsck/k9/activity/InsertableHtmlContent.java similarity index 98% rename from src/com/fsck/k9/mail/internet/InsertableHtmlContent.java rename to src/com/fsck/k9/activity/InsertableHtmlContent.java index fabcc6082..851f0ca9a 100644 --- a/src/com/fsck/k9/mail/internet/InsertableHtmlContent.java +++ b/src/com/fsck/k9/activity/InsertableHtmlContent.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.internet; +package com.fsck.k9.activity; import java.io.Serializable; @@ -12,7 +12,7 @@ import java.io.Serializable; * * TODO: This container should also have a text part, along with its insertion point. Or maybe a generic InsertableContent and maintain one each for Html and Text? */ -public class InsertableHtmlContent implements Serializable { +class InsertableHtmlContent implements Serializable { private static final long serialVersionUID = 2397327034L; // Default to a headerInsertionPoint at the beginning of the message. private int headerInsertionPoint = 0; diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index 30f3e11cb..ffdc95878 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -90,13 +90,12 @@ import com.fsck.k9.fragment.ProgressDialogFragment; import com.fsck.k9.helper.ContactItem; import com.fsck.k9.helper.Contacts; import com.fsck.k9.mail.filter.Base64; -import com.fsck.k9.mail.internet.HtmlConverter; +import com.fsck.k9.helper.HtmlConverter; import com.fsck.k9.helper.IdentityHelper; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Body; import com.fsck.k9.mail.Flag; -import com.fsck.k9.mail.internet.InsertableHtmlContent; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.MessagingException; @@ -108,7 +107,6 @@ import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; -import com.fsck.k9.mail.internet.TextBodyBuilder; import com.fsck.k9.mailstore.LocalAttachmentBody; import com.fsck.k9.mailstore.LocalMessage; import com.fsck.k9.mailstore.TempFileBody; @@ -2957,10 +2955,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, if (messageFormat == MessageFormat.HTML) { - Part part = MimeUtility.findFirstPartByMimeType(message, "text/html"); + Part part = message.findFirstPartByMimeType("text/html"); if (part != null) { // Shouldn't happen if we were the one who saved it. mQuotedTextFormat = SimpleMessageFormat.HTML; - String text = MimeUtility.getTextFromPart(part); + String text = part.getText(); if (K9.DEBUG) { Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + "."); } @@ -3023,9 +3021,9 @@ public class MessageCompose extends K9Activity implements OnClickListener, */ private void processSourceMessageText(Message message, Integer bodyOffset, Integer bodyLength, boolean viewMessageContent) throws MessagingException { - Part textPart = MimeUtility.findFirstPartByMimeType(message, "text/plain"); + Part textPart = message.findFirstPartByMimeType("text/plain"); if (textPart != null) { - String text = MimeUtility.getTextFromPart(textPart); + String text = textPart.getText(); if (K9.DEBUG) { Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + "."); } @@ -3093,7 +3091,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, // Figure out which message format to use for the quoted text by looking if the source // message contains a text/html part. If it does, we use that. mQuotedTextFormat = - (MimeUtility.findFirstPartByMimeType(mSourceMessage, "text/html") == null) ? + (mSourceMessage.findFirstPartByMimeType("text/html") == null) ? SimpleMessageFormat.TEXT : SimpleMessageFormat.HTML; } else { mQuotedTextFormat = SimpleMessageFormat.HTML; @@ -3223,37 +3221,37 @@ public class MessageCompose extends K9Activity implements OnClickListener, Part part; if (format == SimpleMessageFormat.HTML) { // HTML takes precedence, then text. - part = MimeUtility.findFirstPartByMimeType(message, "text/html"); + part = message.findFirstPartByMimeType("text/html"); if (part != null) { if (K9.DEBUG) { Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, HTML found."); } - return MimeUtility.getTextFromPart(part); + return part.getText(); } - part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); + part = message.findFirstPartByMimeType("text/plain"); if (part != null) { if (K9.DEBUG) { Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, text found."); } - return HtmlConverter.textToHtml(MimeUtility.getTextFromPart(part)); + return HtmlConverter.textToHtml(part.getText()); } } else if (format == SimpleMessageFormat.TEXT) { // Text takes precedence, then html. - part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); + part = message.findFirstPartByMimeType("text/plain"); if (part != null) { if (K9.DEBUG) { Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, text found."); } - return MimeUtility.getTextFromPart(part); + return part.getText(); } - part = MimeUtility.findFirstPartByMimeType(message, "text/html"); + part = message.findFirstPartByMimeType("text/html"); if (part != null) { if (K9.DEBUG) { Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, HTML found."); } - return HtmlConverter.htmlToText(MimeUtility.getTextFromPart(part)); + return HtmlConverter.htmlToText(part.getText()); } } diff --git a/src/com/fsck/k9/mail/internet/TextBodyBuilder.java b/src/com/fsck/k9/activity/TextBodyBuilder.java similarity index 98% rename from src/com/fsck/k9/mail/internet/TextBodyBuilder.java rename to src/com/fsck/k9/activity/TextBodyBuilder.java index 962a80e14..4dc0a0666 100644 --- a/src/com/fsck/k9/mail/internet/TextBodyBuilder.java +++ b/src/com/fsck/k9/activity/TextBodyBuilder.java @@ -1,12 +1,14 @@ -package com.fsck.k9.mail.internet; +package com.fsck.k9.activity; import android.text.TextUtils; import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.mail.Body; +import com.fsck.k9.helper.HtmlConverter; +import com.fsck.k9.mail.internet.TextBody; -public class TextBodyBuilder { +class TextBodyBuilder { private boolean mIncludeQuotedText = true; private boolean mReplyAfterQuote = false; private boolean mSignatureBeforeQuotedText = false; diff --git a/src/com/fsck/k9/activity/setup/WelcomeMessage.java b/src/com/fsck/k9/activity/setup/WelcomeMessage.java index e40a95eb9..9c30bb0c2 100644 --- a/src/com/fsck/k9/activity/setup/WelcomeMessage.java +++ b/src/com/fsck/k9/activity/setup/WelcomeMessage.java @@ -12,7 +12,7 @@ import android.widget.TextView; import com.fsck.k9.R; import com.fsck.k9.activity.Accounts; import com.fsck.k9.activity.K9Activity; -import com.fsck.k9.mail.internet.HtmlConverter; +import com.fsck.k9.helper.HtmlConverter; /** * Displays a welcome message when no accounts have been created yet. diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index 1e7e98885..76c151c8a 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -1744,7 +1744,7 @@ public class MessagingController implements Runnable { * right now, attachments will be left for later. */ - Set viewables = MimeUtility.collectTextParts(message); + Set viewables = message.collectTextParts(); /* * Now download the parts we're interested in storing. @@ -3197,7 +3197,7 @@ public class MessagingController implements Runnable { try { LocalStore localStore = account.getLocalStore(); - List attachments = MimeUtility.collectAttachments(message); + List attachments = message.collectAttachments(); for (Part attachment : attachments) { attachment.setBody(null); } @@ -4244,13 +4244,12 @@ public class MessagingController implements Runnable { try { Intent msg = new Intent(Intent.ACTION_SEND); String quotedText = null; - Part part = MimeUtility.findFirstPartByMimeType(message, - "text/plain"); + Part part = message.findFirstPartByMimeType("text/plain"); if (part == null) { - part = MimeUtility.findFirstPartByMimeType(message, "text/html"); + part = message.findFirstPartByMimeType("text/html"); } if (part != null) { - quotedText = MimeUtility.getTextFromPart(part); + quotedText = part.getText(); } if (quotedText != null) { msg.putExtra(Intent.EXTRA_TEXT, quotedText); diff --git a/src/com/fsck/k9/crypto/CryptoHelper.java b/src/com/fsck/k9/crypto/CryptoHelper.java index 3ce2787c7..9bc421127 100644 --- a/src/com/fsck/k9/crypto/CryptoHelper.java +++ b/src/com/fsck/k9/crypto/CryptoHelper.java @@ -32,12 +32,12 @@ public class CryptoHelper { public boolean isEncrypted(Message message) { String data = null; try { - Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); + Part part = message.findFirstPartByMimeType("text/plain"); if (part == null) { - part = MimeUtility.findFirstPartByMimeType(message, "text/html"); + part = message.findFirstPartByMimeType("text/html"); } if (part != null) { - data = MimeUtility.getTextFromPart(part); + data = part.getText(); } } catch (MessagingException e) { // guess not... @@ -55,12 +55,12 @@ public class CryptoHelper { public boolean isSigned(Message message) { String data = null; try { - Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); + Part part = message.findFirstPartByMimeType("text/plain"); if (part == null) { - part = MimeUtility.findFirstPartByMimeType(message, "text/html"); + part = message.findFirstPartByMimeType("text/html"); } if (part != null) { - data = MimeUtility.getTextFromPart(part); + data = part.getText(); } } catch (MessagingException e) { // guess not... diff --git a/src/com/fsck/k9/mail/internet/HtmlConverter.java b/src/com/fsck/k9/helper/HtmlConverter.java similarity index 99% rename from src/com/fsck/k9/mail/internet/HtmlConverter.java rename to src/com/fsck/k9/helper/HtmlConverter.java index 8d28085c1..28bf875b4 100644 --- a/src/com/fsck/k9/mail/internet/HtmlConverter.java +++ b/src/com/fsck/k9/helper/HtmlConverter.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.internet; +package com.fsck.k9.helper; import android.text.*; import android.text.Html.TagHandler; diff --git a/src/com/fsck/k9/mail/BodyPart.java b/src/com/fsck/k9/mail/BodyPart.java index 1118304ba..84e4324f5 100644 --- a/src/com/fsck/k9/mail/BodyPart.java +++ b/src/com/fsck/k9/mail/BodyPart.java @@ -1,6 +1,9 @@ package com.fsck.k9.mail; +import com.fsck.k9.mail.internet.MessageExtractor; +import com.fsck.k9.mail.internet.MimeUtility; + public abstract class BodyPart implements Part { private Multipart mParent; @@ -15,5 +18,23 @@ public abstract class BodyPart implements Part { public abstract void setEncoding(String encoding) throws MessagingException; @Override - public abstract void setUsing7bitTransport() throws MessagingException; + public String getContentDisposition() { + try { + String disposition = getDisposition(); + if (disposition != null) { + return MimeUtility.getHeaderParameter(disposition, null); + } + } catch (MessagingException e) { /* ignore */ } + return null; + } + + @Override + public String getText() { + return MessageExtractor.getTextFromPart(this); + } + + @Override + public Part findFirstPartByMimeType(String mimeType) throws MessagingException { + return MimeUtility.findFirstPartByMimeType(this, mimeType); + } } diff --git a/src/com/fsck/k9/mail/Message.java b/src/com/fsck/k9/mail/Message.java index 24a29af5f..f48eaf559 100644 --- a/src/com/fsck/k9/mail/Message.java +++ b/src/com/fsck/k9/mail/Message.java @@ -2,9 +2,11 @@ package com.fsck.k9.mail; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.EnumSet; +import java.util.List; import java.util.Set; import android.util.Log; @@ -12,6 +14,8 @@ import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.mail.filter.CountingOutputStream; import com.fsck.k9.mail.filter.EOLConvertingOutputStream; +import com.fsck.k9.mail.internet.MessageExtractor; +import com.fsck.k9.mail.internet.MimeUtility; public abstract class Message implements Part, CompositeBody { @@ -265,4 +269,69 @@ public abstract class Message implements Part, CompositeBody { */ @Override public abstract Message clone(); + + /** + * Get the value of the {@code Content-Disposition} header. + * @return The value of the {@code Content-Disposition} header if available. {@code null}, otherwise. + */ + public String getContentDisposition() { + try { + String disposition = getDisposition(); + if (disposition != null) { + return MimeUtility.getHeaderParameter(disposition, null); + } + } + catch (MessagingException e) { /* ignore */ } + return null; + } + + @Override + public String getText() { + return MessageExtractor.getTextFromPart(this); + } + + @Override + public Part findFirstPartByMimeType(String mimeType) throws MessagingException { + return MimeUtility.findFirstPartByMimeType(this, mimeType); + } + + /** + * Collect attachment parts of a message. + * + * @param message + * The message to collect the attachment parts from. + * + * @return A list of parts regarded as attachments. + * + * @throws MessagingException + * In case of an error. + */ + public List collectAttachments() throws MessagingException { + try { + List attachments = new ArrayList(); + MessageExtractor.getViewables(this, attachments); + return attachments; + } catch (Exception e) { + throw new MessagingException("Couldn't collect attachment parts", e); + } + } + + /** + * Collect the viewable textual parts of a message. + * + * @param message + * The message to extract the viewable parts from. + * + * @return A set of viewable parts of the message. + * + * @throws MessagingException + * In case of an error. + */ + public Set collectTextParts() throws MessagingException { + try { + return MessageExtractor.getTextParts(this); + } catch (Exception e) { + throw new MessagingException("Couldn't extract viewable parts", e); + } + } } diff --git a/src/com/fsck/k9/mail/Multipart.java b/src/com/fsck/k9/mail/Multipart.java index 567b0902d..8bcba92b3 100644 --- a/src/com/fsck/k9/mail/Multipart.java +++ b/src/com/fsck/k9/mail/Multipart.java @@ -7,7 +7,7 @@ import java.util.List; import org.apache.james.mime4j.util.MimeUtil; -import com.fsck.k9.mail.internet.MimeUtility; +import com.fsck.k9.mail.internet.CharsetSupport; import com.fsck.k9.mail.internet.TextBody; public abstract class Multipart implements CompositeBody { @@ -64,7 +64,7 @@ public abstract class Multipart implements CompositeBody { BodyPart part = mParts.get(0); Body body = part.getBody(); if (body instanceof TextBody) { - MimeUtility.setCharset(charset, part); + CharsetSupport.setCharset(charset, part); ((TextBody)body).setCharset(charset); } } diff --git a/src/com/fsck/k9/mail/Part.java b/src/com/fsck/k9/mail/Part.java index 38c1ad9da..c890cd56e 100644 --- a/src/com/fsck/k9/mail/Part.java +++ b/src/com/fsck/k9/mail/Part.java @@ -5,29 +5,41 @@ import java.io.IOException; import java.io.OutputStream; public interface Part { - public void addHeader(String name, String value) throws MessagingException; + void addHeader(String name, String value) throws MessagingException; - public void removeHeader(String name) throws MessagingException; + void removeHeader(String name) throws MessagingException; - public void setHeader(String name, String value) throws MessagingException; + void setHeader(String name, String value) throws MessagingException; - public Body getBody(); + Body getBody(); - public String getContentType() throws MessagingException; + String getContentType() throws MessagingException; - public String getDisposition() throws MessagingException; + String getDisposition() throws MessagingException; - public String getContentId() throws MessagingException; + String getContentDisposition(); - public String[] getHeader(String name) throws MessagingException; + String getContentId() throws MessagingException; - public boolean isMimeType(String mimeType) throws MessagingException; + String[] getHeader(String name) throws MessagingException; - public String getMimeType() throws MessagingException; + boolean isMimeType(String mimeType) throws MessagingException; - public void setBody(Body body) throws MessagingException; + String getMimeType() throws MessagingException; - public void writeTo(OutputStream out) throws IOException, MessagingException; + void setBody(Body body) throws MessagingException; + + void writeTo(OutputStream out) throws IOException, MessagingException; + + /** + * Reads the Part's body and returns a String based on any charset conversion that needed + * to be done. Note, this does not return a text representation of HTML. + * @return a String containing the converted text in the body, or null if there was no text + * or an error during conversion. + */ + String getText(); + + Part findFirstPartByMimeType(String mimeType) throws MessagingException; /** * Called just prior to transmission, once the type of transport is known to @@ -41,5 +53,5 @@ public interface Part { * */ //TODO perhaps it would be clearer to use a flag "force7bit" in writeTo - public abstract void setUsing7bitTransport() throws MessagingException; + void setUsing7bitTransport() throws MessagingException; } diff --git a/src/com/fsck/k9/mail/internet/CharsetSupport.java b/src/com/fsck/k9/mail/internet/CharsetSupport.java new file mode 100644 index 000000000..5346566f3 --- /dev/null +++ b/src/com/fsck/k9/mail/internet/CharsetSupport.java @@ -0,0 +1,1121 @@ +package com.fsck.k9.mail.internet; + +import android.util.Log; + +import com.fsck.k9.K9; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.Part; + +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.util.Locale; + +import static com.fsck.k9.mail.internet.JisSupport.SHIFT_JIS; + +public class CharsetSupport { + /** + * Table for character set fall-back. + * + * Table format: unsupported charset (regular expression), fall-back charset + */ + private static final String[][] CHARSET_FALLBACK_MAP = new String[][] { + // Some Android versions don't support KOI8-U + {"koi8-u", "koi8-r"}, + {"iso-2022-jp-[\\d]+", "iso-2022-jp"}, + // Default fall-back is US-ASCII + {".*", "US-ASCII"} + }; + + + public static void setCharset(String charset, Part part) throws MessagingException { + part.setHeader(MimeHeader.HEADER_CONTENT_TYPE, + part.getMimeType() + ";\r\n charset=" + getExternalCharset(charset)); + } + + + public static String getCharsetFromAddress(String address) { + String variant = JisSupport.getJisVariantFromAddress(address); + if (variant != null) { + String charset = "x-" + variant + "-shift_jis-2007"; + if (Charset.isSupported(charset)) + return charset; + } + + return "UTF-8"; + } + + static String getExternalCharset(String charset) { + if (JisSupport.isShiftJis(charset)) { + return SHIFT_JIS; + } else { + return charset; + } + } + + static String fixupCharset(String charset, Message message) throws MessagingException { + if (charset == null || "0".equals(charset)) + charset = "US-ASCII"; // No encoding, so use us-ascii, which is the standard. + + charset = charset.toLowerCase(Locale.US); + if (charset.equals("cp932")) + charset = SHIFT_JIS; + + if (charset.equals(SHIFT_JIS) || charset.equals("iso-2022-jp")) { + String variant = JisSupport.getJisVariantFromMessage(message); + if (variant != null) + charset = "x-" + variant + "-" + charset + "-2007"; + } + return charset; + } + + + static String readToString(InputStream in, String charset) throws IOException { + boolean isIphoneString = false; + + // iso-2022-jp variants are supported by no versions as of Dec 2010. + if (charset.length() > 19 && charset.startsWith("x-") && + charset.endsWith("-iso-2022-jp-2007") && !Charset.isSupported(charset)) { + in = new Iso2022JpToShiftJisInputStream(in); + charset = "x-" + charset.substring(2, charset.length() - 17) + "-shift_jis-2007"; + } + + // shift_jis variants are supported by Eclair and later. + if (JisSupport.isShiftJis(charset) && !Charset.isSupported(charset)) { + // If the JIS variant is iPhone, map the Unicode private use area in iPhone to the one in Android after + // converting the character set from the standard Shift JIS to Unicode. + if (charset.substring(2, charset.length() - 15).equals("iphone")) + isIphoneString = true; + + charset = SHIFT_JIS; + } + + /* + * See if there is conversion from the MIME charset to the Java one. + * this function may also throw an exception if the charset name is not known + */ + boolean supported; + try { + supported = Charset.isSupported(charset); + } catch (IllegalCharsetNameException e) { + supported = false; + } + + for (String[] rule: CHARSET_FALLBACK_MAP) { + if (supported) { + break; + } + + if (charset.matches(rule[0])) { + Log.e(K9.LOG_TAG, "I don't know how to deal with the charset " + charset + + ". Falling back to " + rule[1]); + charset = rule[1]; + try { + supported = Charset.isSupported(charset); + } catch (IllegalCharsetNameException e) { + supported = false; + } + } + } + + /* + * Convert and return as new String + */ + String str = IOUtils.toString(in, charset); + + if (isIphoneString) + str = importStringFromIphone(str); + return str; + } + + private static String importStringFromIphone(String str) { + StringBuilder buff = new StringBuilder(str.length()); + for (int i = 0; i < str.length(); i = str.offsetByCodePoints(i, 1)) { + int codePoint = str.codePointAt(i); + buff.appendCodePoint(importCodePointFromIphone(codePoint)); + } + return buff.toString(); + } + + private static int importCodePointFromIphone(int codePoint) { + switch (codePoint) { + case 0xE001: + return 0xFE19B; + case 0xE002: + return 0xFE19C; + case 0xE003: + return 0xFE823; + case 0xE004: + return 0xFE19D; + case 0xE005: + return 0xFE19E; + case 0xE006: + return 0xFE4CF; + case 0xE007: + return 0xFE4CD; + case 0xE008: + return 0xFE4EF; + case 0xE009: + return 0xFE523; + case 0xE00A: + return 0xFE525; + case 0xE00B: + return 0xFE528; + case 0xE00C: + return 0xFE538; + case 0xE00D: + return 0xFEB96; + case 0xE00E: + return 0xFEB97; + case 0xE00F: + return 0xFEB98; + case 0xE010: + return 0xFEB93; + case 0xE011: + return 0xFEB94; + case 0xE012: + return 0xFEB95; + case 0xE013: + return 0xFE7D5; + case 0xE014: + return 0xFE7D2; + case 0xE015: + return 0xFE7D3; + case 0xE016: + return 0xFE7D1; + case 0xE017: + return 0xFE7DA; + case 0xE018: + return 0xFE7D4; + case 0xE019: + return 0xFE1BD; + case 0xE01A: + return 0xFE1BE; + case 0xE01B: + return 0xFE7E4; + case 0xE01C: + return 0xFE7EA; + case 0xE01D: + return 0xFE7E9; + case 0xE01E: + return 0xFE7DF; + case 0xE01F: + return 0xFE7E3; + case 0xE020: + return 0xFEB09; + case 0xE021: + return 0xFEB04; + case 0xE022: + return 0xFEB0C; + case 0xE023: + return 0xFEB0E; + case 0xE024: + return 0xFE01E; + case 0xE025: + return 0xFE01F; + case 0xE026: + return 0xFE020; + case 0xE027: + return 0xFE021; + case 0xE028: + return 0xFE022; + case 0xE029: + return 0xFE023; + case 0xE02A: + return 0xFE024; + case 0xE02B: + return 0xFE025; + case 0xE02C: + return 0xFE026; + case 0xE02D: + return 0xFE027; + case 0xE02E: + return 0xFE028; + case 0xE02F: + return 0xFE029; + case 0xE030: + return 0xFE040; + case 0xE031: + return 0xFE4D2; + case 0xE032: + return 0xFE041; + case 0xE033: + return 0xFE512; + case 0xE034: + return 0xFE825; + case 0xE035: + return 0xFE826; + case 0xE036: + return 0xFE4B0; + case 0xE037: + return 0xFE4BB; + case 0xE038: + return 0xFE4B2; + case 0xE039: + return 0xFE7EC; + case 0xE03A: + return 0xFE7F5; + case 0xE03B: + return 0xFE4C3; + case 0xE03C: + return 0xFE800; + case 0xE03D: + return 0xFE801; + case 0xE03E: + return 0xFE813; + case 0xE03F: + return 0xFEB82; + case 0xE040: + return 0xFE815; + case 0xE041: + return 0xFE816; + case 0xE042: + return 0xFE818; + case 0xE043: + return 0xFE980; + case 0xE044: + return 0xFE982; + case 0xE045: + return 0xFE981; + case 0xE046: + return 0xFE962; + case 0xE047: + return 0xFE983; + case 0xE048: + return 0xFE003; + case 0xE049: + return 0xFE001; + case 0xE04A: + return 0xFE000; + case 0xE04B: + return 0xFE002; + case 0xE04C: + return 0xFE014; + case 0xE04D: + return 0xFE009; + case 0xE04E: + return 0xFE1AF; + case 0xE04F: + return 0xFE1B8; + case 0xE050: + return 0xFE1C0; + case 0xE051: + return 0xFE1C1; + case 0xE052: + return 0xFE1B7; + case 0xE053: + return 0xFE1C2; + case 0xE054: + return 0xFE1C3; + case 0xE055: + return 0xFE1BC; + case 0xE056: + return 0xFE335; + case 0xE057: + return 0xFE330; + case 0xE058: + return 0xFE323; + case 0xE059: + return 0xFE320; + case 0xE05A: + return 0xFE4F4; + case 0xE101: + return 0xFE52D; + case 0xE102: + return 0xFE52E; + case 0xE103: + return 0xFE52B; + case 0xE104: + return 0xFE526; + case 0xE105: + return 0xFE329; + case 0xE106: + return 0xFE327; + case 0xE107: + return 0xFE341; + case 0xE108: + return 0xFE344; + case 0xE109: + return 0xFE1C4; + case 0xE10A: + return 0xFE1C5; + case 0xE10B: + return 0xFE1BF; + case 0xE10C: + return 0xFE1B0; + case 0xE10D: + return 0xFE7ED; + case 0xE10E: + return 0xFE4D1; + case 0xE10F: + return 0xFEB56; + case 0xE110: + return 0xFE03C; + case 0xE111: + return 0xFE827; + case 0xE112: + return 0xFE510; + case 0xE113: + return 0xFE4F5; + case 0xE114: + return 0xFEB85; + case 0xE115: + return 0xFE7D9; + case 0xE116: + return 0xFE4CA; + case 0xE117: + return 0xFE515; + case 0xE118: + return 0xFE03F; + case 0xE119: + return 0xFE042; + case 0xE11A: + return 0xFE1B2; + case 0xE11B: + return 0xFE1AE; + case 0xE11C: + return 0xFE1B3; + case 0xE11D: + return 0xFE4F6; + case 0xE11E: + return 0xFE53B; + case 0xE11F: + return 0xFE537; + case 0xE120: + return 0xFE960; + case 0xE121: + return 0xFE4BC; + case 0xE122: + return 0xFE7FB; + case 0xE123: + return 0xFE7FA; + case 0xE124: + return 0xFE7FD; + case 0xE125: + return 0xFE807; + case 0xE126: + return 0xFE81D; + case 0xE127: + return 0xFE81E; + case 0xE128: + return 0xFE81F; + case 0xE129: + return 0xFE820; + case 0xE12A: + return 0xFE81C; + case 0xE12B: + return 0xFE1B1; + case 0xE12C: + return 0xFE81B; + case 0xE12D: + return 0xFE80B; + case 0xE12E: + return 0xFEB32; + case 0xE12F: + return 0xFE4DD; + case 0xE130: + return 0xFE80C; + case 0xE131: + return 0xFE7DB; + case 0xE132: + return 0xFE7D7; + case 0xE133: + return 0xFE80D; + case 0xE134: + return 0xFE7DC; + case 0xE135: + return 0xFE7EE; + case 0xE136: + return 0xFE7EB; + case 0xE137: + return 0xFE7F8; + case 0xE138: + return 0xFEB33; + case 0xE139: + return 0xFEB34; + case 0xE13A: + return 0xFEB35; + case 0xE13B: + return 0xFE509; + case 0xE13C: + return 0xFEB59; + case 0xE13D: + return 0xFE004; + case 0xE13E: + return 0xFE4D6; + case 0xE13F: + return 0xFE505; + case 0xE140: + return 0xFE507; + case 0xE141: + return 0xFE821; + case 0xE142: + return 0xFE52F; + case 0xE143: + return 0xFE514; + case 0xE144: + return 0xFEB86; + case 0xE145: + return 0xFEB87; + case 0xE146: + return 0xFE00B; + case 0xE147: + return 0xFE965; + case 0xE148: + return 0xFE546; + case 0xE149: + return 0xFE4DE; + case 0xE14A: + return 0xFE4DF; + case 0xE14B: + return 0xFE531; + case 0xE14C: + return 0xFEB5E; + case 0xE14D: + return 0xFE4B5; + case 0xE14E: + return 0xFE7F7; + case 0xE14F: + return 0xFE7F6; + case 0xE150: + return 0xFE7E7; + case 0xE151: + return 0xFE506; + case 0xE152: + return 0xFE1A1; + case 0xE153: + return 0xFE4B3; + case 0xE154: + return 0xFE4B6; + case 0xE155: + return 0xFE4B4; + case 0xE156: + return 0xFE4B9; + case 0xE157: + return 0xFE4BA; + case 0xE158: + return 0xFE4B7; + case 0xE159: + return 0xFE7E6; + case 0xE15A: + return 0xFE7EF; + case 0xE201: + return 0xFE7F0; + case 0xE202: + return 0xFE7E8; + case 0xE203: + return 0xFEB24; + case 0xE204: + return 0xFEB19; + case 0xE205: + return 0xFEB61; + case 0xE206: + return 0xFEB62; + case 0xE207: + return 0xFEB25; + case 0xE208: + return 0xFEB1F; + case 0xE209: + return 0xFE044; + case 0xE20A: + return 0xFEB20; + case 0xE20B: + return 0xFE838; + case 0xE20C: + return 0xFEB1A; + case 0xE20D: + return 0xFEB1C; + case 0xE20E: + return 0xFEB1B; + case 0xE20F: + return 0xFEB1D; + case 0xE210: + return 0xFE82C; + case 0xE211: + return 0xFE82B; + case 0xE212: + return 0xFEB36; + case 0xE213: + return 0xFEB37; + case 0xE214: + return 0xFEB38; + case 0xE215: + return 0xFEB39; + case 0xE216: + return 0xFEB3A; + case 0xE217: + return 0xFEB3B; + case 0xE218: + return 0xFEB3C; + case 0xE219: + return 0xFEB63; + case 0xE21A: + return 0xFEB64; + case 0xE21B: + return 0xFEB67; + case 0xE21C: + return 0xFE82E; + case 0xE21D: + return 0xFE82F; + case 0xE21E: + return 0xFE830; + case 0xE21F: + return 0xFE831; + case 0xE220: + return 0xFE832; + case 0xE221: + return 0xFE833; + case 0xE222: + return 0xFE834; + case 0xE223: + return 0xFE835; + case 0xE224: + return 0xFE836; + case 0xE225: + return 0xFE837; + case 0xE226: + return 0xFEB3D; + case 0xE227: + return 0xFEB3E; + case 0xE228: + return 0xFEB3F; + case 0xE229: + return 0xFEB81; + case 0xE22A: + return 0xFEB31; + case 0xE22B: + return 0xFEB2F; + case 0xE22C: + return 0xFEB40; + case 0xE22D: + return 0xFEB41; + case 0xE22E: + return 0xFEB99; + case 0xE22F: + return 0xFEB9A; + case 0xE230: + return 0xFEB9B; + case 0xE231: + return 0xFEB9C; + case 0xE232: + return 0xFEAF8; + case 0xE233: + return 0xFEAF9; + case 0xE234: + return 0xFEAFA; + case 0xE235: + return 0xFEAFB; + case 0xE236: + return 0xFEAF0; + case 0xE237: + return 0xFEAF2; + case 0xE238: + return 0xFEAF1; + case 0xE239: + return 0xFEAF3; + case 0xE23A: + return 0xFEAFC; + case 0xE23B: + return 0xFEAFD; + case 0xE23C: + return 0xFEAFE; + case 0xE23D: + return 0xFEAFF; + case 0xE23E: + return 0xFE4F8; + case 0xE23F: + return 0xFE02B; + case 0xE240: + return 0xFE02C; + case 0xE241: + return 0xFE02D; + case 0xE242: + return 0xFE02E; + case 0xE243: + return 0xFE02F; + case 0xE244: + return 0xFE030; + case 0xE245: + return 0xFE031; + case 0xE246: + return 0xFE032; + case 0xE247: + return 0xFE033; + case 0xE248: + return 0xFE034; + case 0xE249: + return 0xFE035; + case 0xE24A: + return 0xFE036; + case 0xE24B: + return 0xFE037; + case 0xE24C: + return 0xFEB42; + case 0xE24D: + return 0xFEB27; + case 0xE24E: + return 0xFEB29; + case 0xE24F: + return 0xFEB2D; + case 0xE250: + return 0xFE839; + case 0xE251: + return 0xFE83A; + case 0xE252: + return 0xFEB23; + case 0xE253: + return 0xFE1B4; + case 0xE254: + return 0xFEE77; + case 0xE255: + return 0xFEE78; + case 0xE256: + return 0xFEE79; + case 0xE257: + return 0xFEE7A; + case 0xE258: + return 0xFEE7B; + case 0xE259: + return 0xFEE7C; + case 0xE25A: + return 0xFEE7D; + case 0xE301: + return 0xFE527; + case 0xE302: + return 0xFE4D3; + case 0xE303: + return 0xFE045; + case 0xE304: + return 0xFE03D; + case 0xE305: + return 0xFE046; + case 0xE306: + return 0xFE828; + case 0xE307: + return 0xFE047; + case 0xE308: + return 0xFE048; + case 0xE309: + return 0xFE508; + case 0xE30A: + return 0xFE803; + case 0xE30B: + return 0xFE985; + case 0xE30C: + return 0xFE987; + case 0xE30D: + return 0xFEB43; + case 0xE30E: + return 0xFEB1E; + case 0xE30F: + return 0xFE50A; + case 0xE310: + return 0xFE516; + case 0xE311: + return 0xFEB58; + case 0xE312: + return 0xFE517; + case 0xE313: + return 0xFE53E; + case 0xE314: + return 0xFE50F; + case 0xE315: + return 0xFEB2B; + case 0xE316: + return 0xFE53C; + case 0xE317: + return 0xFE530; + case 0xE318: + return 0xFE4D4; + case 0xE319: + return 0xFE4D5; + case 0xE31A: + return 0xFE4D7; + case 0xE31B: + return 0xFE4D8; + case 0xE31C: + return 0xFE195; + case 0xE31D: + return 0xFE196; + case 0xE31E: + return 0xFE197; + case 0xE31F: + return 0xFE198; + case 0xE320: + return 0xFE199; + case 0xE321: + return 0xFE4D9; + case 0xE322: + return 0xFE4DA; + case 0xE323: + return 0xFE4F0; + case 0xE324: + return 0xFE808; + case 0xE325: + return 0xFE4F2; + case 0xE326: + return 0xFE814; + case 0xE327: + return 0xFEB0D; + case 0xE328: + return 0xFEB11; + case 0xE329: + return 0xFEB12; + case 0xE32A: + return 0xFEB13; + case 0xE32B: + return 0xFEB14; + case 0xE32C: + return 0xFEB15; + case 0xE32D: + return 0xFEB16; + case 0xE32E: + return 0xFEB60; + case 0xE32F: + return 0xFEB68; + case 0xE330: + return 0xFEB5D; + case 0xE331: + return 0xFEB5B; + case 0xE332: + return 0xFEB44; + case 0xE333: + return 0xFEB45; + case 0xE334: + return 0xFEB57; + case 0xE335: + return 0xFEB69; + case 0xE336: + return 0xFEB0A; + case 0xE337: + return 0xFEB0B; + case 0xE338: + return 0xFE984; + case 0xE339: + return 0xFE964; + case 0xE33A: + return 0xFE966; + case 0xE33B: + return 0xFE967; + case 0xE33C: + return 0xFE968; + case 0xE33D: + return 0xFE969; + case 0xE33E: + return 0xFE96A; + case 0xE33F: + return 0xFE96B; + case 0xE340: + return 0xFE963; + case 0xE341: + return 0xFE96C; + case 0xE342: + return 0xFE961; + case 0xE343: + return 0xFE96D; + case 0xE344: + return 0xFE96E; + case 0xE345: + return 0xFE051; + case 0xE346: + return 0xFE052; + case 0xE347: + return 0xFE053; + case 0xE348: + return 0xFE054; + case 0xE349: + return 0xFE055; + case 0xE34A: + return 0xFE056; + case 0xE34B: + return 0xFE511; + case 0xE34C: + return 0xFE96F; + case 0xE34D: + return 0xFE970; + case 0xE401: + return 0xFE345; + case 0xE402: + return 0xFE343; + case 0xE403: + return 0xFE340; + case 0xE404: + return 0xFE333; + case 0xE405: + return 0xFE347; + case 0xE406: + return 0xFE33C; + case 0xE407: + return 0xFE33F; + case 0xE408: + return 0xFE342; + case 0xE409: + return 0xFE32A; + case 0xE40A: + return 0xFE33E; + case 0xE40B: + return 0xFE33B; + case 0xE40C: + return 0xFE32E; + case 0xE40D: + return 0xFE32F; + case 0xE40E: + return 0xFE326; + case 0xE40F: + return 0xFE325; + case 0xE410: + return 0xFE322; + case 0xE411: + return 0xFE33A; + case 0xE412: + return 0xFE334; + case 0xE413: + return 0xFE339; + case 0xE414: + return 0xFE336; + case 0xE415: + return 0xFE338; + case 0xE416: + return 0xFE33D; + case 0xE417: + return 0xFE32D; + case 0xE418: + return 0xFE32C; + case 0xE419: + return 0xFE190; + case 0xE41A: + return 0xFE192; + case 0xE41B: + return 0xFE191; + case 0xE41C: + return 0xFE193; + case 0xE41D: + return 0xFE35B; + case 0xE41E: + return 0xFEB9D; + case 0xE41F: + return 0xFEB9E; + case 0xE420: + return 0xFEB9F; + case 0xE421: + return 0xFEBA0; + case 0xE422: + return 0xFEBA1; + case 0xE423: + return 0xFE351; + case 0xE424: + return 0xFE352; + case 0xE425: + return 0xFE829; + case 0xE426: + return 0xFE353; + case 0xE427: + return 0xFE358; + case 0xE428: + return 0xFE1A0; + case 0xE429: + return 0xFE1A2; + case 0xE42A: + return 0xFE7D6; + case 0xE42B: + return 0xFE7DD; + case 0xE42C: + return 0xFE80E; + case 0xE42D: + return 0xFE7DE; + case 0xE42E: + return 0xFE7E5; + case 0xE42F: + return 0xFE7F1; + case 0xE430: + return 0xFE7F2; + case 0xE431: + return 0xFE7F3; + case 0xE432: + return 0xFE7F4; + case 0xE433: + return 0xFE7FE; + case 0xE434: + return 0xFE7E0; + case 0xE435: + return 0xFE7E2; + case 0xE436: + return 0xFE518; + case 0xE437: + return 0xFEB17; + case 0xE438: + return 0xFE519; + case 0xE439: + return 0xFE51A; + case 0xE43A: + return 0xFE51B; + case 0xE43B: + return 0xFE51C; + case 0xE43C: + return 0xFE007; + case 0xE43D: + return 0xFE82A; + case 0xE43E: + return 0xFE038; + case 0xE43F: + return 0xFE971; + case 0xE440: + return 0xFE51D; + case 0xE441: + return 0xFE1C6; + case 0xE442: + return 0xFE51E; + case 0xE443: + return 0xFE005; + case 0xE444: + return 0xFE049; + case 0xE445: + return 0xFE51F; + case 0xE446: + return 0xFE017; + case 0xE447: + return 0xFE043; + case 0xE448: + return 0xFE513; + case 0xE449: + return 0xFE00A; + case 0xE44A: + return 0xFE00C; + case 0xE44B: + return 0xFE008; + case 0xE44C: + return 0xFE00D; + case 0xE501: + return 0xFE4B8; + case 0xE502: + return 0xFE804; + case 0xE503: + return 0xFE805; + case 0xE504: + return 0xFE4BD; + case 0xE505: + return 0xFE4BE; + case 0xE506: + return 0xFE4BF; + case 0xE507: + return 0xFE802; + case 0xE508: + return 0xFE4C0; + case 0xE509: + return 0xFE4C4; + case 0xE50A: + return 0xFE4C5; + case 0xE50B: + return 0xFE4E5; + case 0xE50C: + return 0xFE4E6; + case 0xE50D: + return 0xFE4E7; + case 0xE50E: + return 0xFE4E8; + case 0xE50F: + return 0xFE4E9; + case 0xE510: + return 0xFE4EA; + case 0xE511: + return 0xFE4EB; + case 0xE512: + return 0xFE4EC; + case 0xE513: + return 0xFE4ED; + case 0xE514: + return 0xFE4EE; + case 0xE515: + return 0xFE1A4; + case 0xE516: + return 0xFE1A5; + case 0xE517: + return 0xFE1A6; + case 0xE518: + return 0xFE1A7; + case 0xE519: + return 0xFE1A8; + case 0xE51A: + return 0xFE1A9; + case 0xE51B: + return 0xFE1AA; + case 0xE51C: + return 0xFE1AB; + case 0xE51D: + return 0xFE4C6; + case 0xE51E: + return 0xFE1B5; + case 0xE51F: + return 0xFE1B6; + case 0xE520: + return 0xFE1C7; + case 0xE521: + return 0xFE1C8; + case 0xE522: + return 0xFE1C9; + case 0xE523: + return 0xFE1BA; + case 0xE524: + return 0xFE1CA; + case 0xE525: + return 0xFE1CB; + case 0xE526: + return 0xFE1CC; + case 0xE527: + return 0xFE1CD; + case 0xE528: + return 0xFE1CE; + case 0xE529: + return 0xFE1CF; + case 0xE52A: + return 0xFE1D0; + case 0xE52B: + return 0xFE1D1; + case 0xE52C: + return 0xFE1D2; + case 0xE52D: + return 0xFE1D3; + case 0xE52E: + return 0xFE1D4; + case 0xE52F: + return 0xFE1D5; + case 0xE530: + return 0xFE1D6; + case 0xE531: + return 0xFE1D7; + case 0xE532: + return 0xFE50B; + case 0xE533: + return 0xFE50C; + case 0xE534: + return 0xFE50D; + case 0xE535: + return 0xFE50E; + case 0xE536: + return 0xFE553; + case 0xE537: + return 0xFEB2A; + case 0xE538: + return 0xFEE70; + case 0xE539: + return 0xFEE71; + case 0xE53A: + return 0xFEE72; + case 0xE53B: + return 0xFEE73; + case 0xE53C: + return 0xFEE74; + case 0xE53D: + return 0xFEE75; + case 0xE53E: + return 0xFEE76; + default: + return codePoint; + } + } + +} diff --git a/src/com/fsck/k9/mail/internet/DecoderUtil.java b/src/com/fsck/k9/mail/internet/DecoderUtil.java index 3ac74947d..3e439ab99 100644 --- a/src/com/fsck/k9/mail/internet/DecoderUtil.java +++ b/src/com/fsck/k9/mail/internet/DecoderUtil.java @@ -35,7 +35,7 @@ class DecoderUtil { Base64InputStream is = new Base64InputStream(new ByteArrayInputStream(bytes)); try { - return MimeUtility.readToString(is, charset); + return CharsetSupport.readToString(is, charset); } catch (IOException e) { return null; } @@ -68,7 +68,7 @@ class DecoderUtil { QuotedPrintableInputStream is = new QuotedPrintableInputStream(new ByteArrayInputStream(bytes)); try { - return MimeUtility.readToString(is, charset); + return CharsetSupport.readToString(is, charset); } catch (IOException e) { return null; } @@ -162,7 +162,7 @@ class DecoderUtil { String charset; try { - charset = MimeUtility.fixupCharset(mimeCharset, message); + charset = CharsetSupport.fixupCharset(mimeCharset, message); } catch (MessagingException e) { return null; } diff --git a/src/com/fsck/k9/mail/internet/EncoderUtil.java b/src/com/fsck/k9/mail/internet/EncoderUtil.java index 5e7672715..2c71ecc2d 100644 --- a/src/com/fsck/k9/mail/internet/EncoderUtil.java +++ b/src/com/fsck/k9/mail/internet/EncoderUtil.java @@ -68,7 +68,7 @@ class EncoderUtil { if (charset == null) charset = determineCharset(text); - String mimeCharset = MimeUtility.getExternalCharset(charset.name()); + String mimeCharset = CharsetSupport.getExternalCharset(charset.name()); byte[] bytes = encode(text, charset); diff --git a/src/com/fsck/k9/mail/internet/JisSupport.java b/src/com/fsck/k9/mail/internet/JisSupport.java new file mode 100644 index 000000000..a6a30329a --- /dev/null +++ b/src/com/fsck/k9/mail/internet/JisSupport.java @@ -0,0 +1,103 @@ +package com.fsck.k9.mail.internet; + +import com.fsck.k9.mail.Address; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.Part; + +class JisSupport { + public static final String SHIFT_JIS = "shift_jis"; + + public static String getJisVariantFromMessage(Message message) throws MessagingException { + if (message == null) + return null; + + // If a receiver is known to use a JIS variant, the sender transfers the message after converting the + // charset as a convention. + String variant = getJisVariantFromReceivedHeaders(message); + if (variant != null) + return variant; + + // If a receiver is not known to use any JIS variants, the sender transfers the message without converting + // the charset. + variant = getJisVariantFromFromHeaders(message); + if (variant != null) + return variant; + + return getJisVariantFromMailerHeaders(message); + } + + public static boolean isShiftJis(String charset) { + return charset.length() > 17 && charset.startsWith("x-") + && charset.endsWith("-shift_jis-2007"); + } + + public static String getJisVariantFromAddress(String address) { + if (address == null) + return null; + if (isInDomain(address, "docomo.ne.jp") || isInDomain(address, "dwmail.jp") || + isInDomain(address, "pdx.ne.jp") || isInDomain(address, "willcom.com") || + isInDomain(address, "emnet.ne.jp") || isInDomain(address, "emobile.ne.jp")) + return "docomo"; + else if (isInDomain(address, "softbank.ne.jp") || isInDomain(address, "vodafone.ne.jp") || + isInDomain(address, "disney.ne.jp") || isInDomain(address, "vertuclub.ne.jp")) + return "softbank"; + else if (isInDomain(address, "ezweb.ne.jp") || isInDomain(address, "ido.ne.jp")) + return "kddi"; + return null; + } + + + private static String getJisVariantFromMailerHeaders(Message message) throws MessagingException { + String mailerHeaders[] = message.getHeader("X-Mailer"); + if (mailerHeaders == null || mailerHeaders.length == 0) + return null; + + if (mailerHeaders[0].startsWith("iPhone Mail ") || mailerHeaders[0].startsWith("iPad Mail ")) + return "iphone"; + + return null; + } + + + private static String getJisVariantFromReceivedHeaders(Part message) throws MessagingException { + String receivedHeaders[] = message.getHeader("Received"); + if (receivedHeaders == null) + return null; + + for (String receivedHeader : receivedHeaders) { + String address = getAddressFromReceivedHeader(receivedHeader); + if (address == null) + continue; + String variant = getJisVariantFromAddress(address); + if (variant != null) + return variant; + } + return null; + } + + private static String getAddressFromReceivedHeader(String receivedHeader) { + // Not implemented yet! Extract an address from the FOR clause of the given Received header. + return null; + } + + private static String getJisVariantFromFromHeaders(Message message) throws MessagingException { + Address addresses[] = message.getFrom(); + if (addresses == null || addresses.length == 0) + return null; + + return getJisVariantFromAddress(addresses[0].getAddress()); + } + + private static boolean isInDomain(String address, String domain) { + int index = address.length() - domain.length() - 1; + if (index < 0) + return false; + + char c = address.charAt(index); + if (c != '@' && c != '.') + return false; + + return address.endsWith(domain); + } +} diff --git a/src/com/fsck/k9/mail/internet/MessageExtractor.java b/src/com/fsck/k9/mail/internet/MessageExtractor.java new file mode 100644 index 000000000..5fedd3d28 --- /dev/null +++ b/src/com/fsck/k9/mail/internet/MessageExtractor.java @@ -0,0 +1,410 @@ +package com.fsck.k9.mail.internet; + +import android.util.Log; + +import com.fsck.k9.K9; +import com.fsck.k9.mail.Body; +import com.fsck.k9.mail.BodyPart; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.Multipart; +import com.fsck.k9.mail.Part; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.fsck.k9.mail.internet.CharsetSupport.fixupCharset; +import static com.fsck.k9.mail.internet.MimeUtility.getHeaderParameter; +import static com.fsck.k9.mail.internet.Viewable.Alternative; +import static com.fsck.k9.mail.internet.Viewable.Textual; + +public class MessageExtractor { + public static String getTextFromPart(Part part) { + try { + if ((part != null) && (part.getBody() != null)) { + final Body body = part.getBody(); + if (body instanceof TextBody) { + return ((TextBody)body).getText(); + } + + final String mimeType = part.getMimeType(); + if ((mimeType != null) && MimeUtility.mimeTypeMatches(mimeType, "text/*")) { + /* + * We've got a text part, so let's see if it needs to be processed further. + */ + String charset = getHeaderParameter(part.getContentType(), "charset"); + /* + * determine the charset from HTML message. + */ + if (mimeType.equalsIgnoreCase("text/html") && charset == null) { + InputStream in = part.getBody().getInputStream(); + try { + byte[] buf = new byte[256]; + in.read(buf, 0, buf.length); + String str = new String(buf, "US-ASCII"); + + if (str.isEmpty()) { + return ""; + } + Pattern p = Pattern.compile("", Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(str); + if (m.find()) { + charset = m.group(1); + } + } finally { + try { + if (in instanceof BinaryTempFileBody.BinaryTempFileBodyInputStream) { + /* + * If this is a BinaryTempFileBodyInputStream, calling close() + * will delete the file. But we can't let that happen because + * the file needs to be opened again by the code a few lines + * down. + */ + ((BinaryTempFileBody.BinaryTempFileBodyInputStream) in).closeWithoutDeleting(); + } else { + in.close(); + } + } catch (Exception e) { /* ignore */ } + } + } + charset = fixupCharset(charset, getMessageFromPart(part)); + + /* + * Now we read the part into a buffer for further processing. Because + * the stream is now wrapped we'll remove any transfer encoding at this point. + */ + InputStream in = part.getBody().getInputStream(); + try { + String text = CharsetSupport.readToString(in, charset); + + // Replace the body with a TextBody that already contains the decoded text + part.setBody(new TextBody(text)); + + return text; + } finally { + try { + /* + * This time we don't care if it's a BinaryTempFileBodyInputStream. We + * replaced the body with a TextBody instance and hence don't need the + * file anymore. + */ + in.close(); + } catch (IOException e) { /* Ignore */ } + } + } + } + + } catch (OutOfMemoryError oom) { + /* + * If we are not able to process the body there's nothing we can do about it. Return + * null and let the upper layers handle the missing content. + */ + Log.e(K9.LOG_TAG, "Unable to getTextFromPart " + oom.toString()); + } catch (Exception e) { + /* + * If we are not able to process the body there's nothing we can do about it. Return + * null and let the upper layers handle the missing content. + */ + Log.e(K9.LOG_TAG, "Unable to getTextFromPart", e); + } + return null; + } + + + /** + * Traverse the MIME tree of a message an extract viewable parts. + * + * @param part + * The message part to start from. + * @param attachments + * A list that will receive the parts that are considered attachments. + * + * @return A list of {@link Viewable}s. + * + * @throws MessagingException + * In case of an error. + */ + public static List getViewables(Part part, List attachments) throws MessagingException { + List viewables = new ArrayList(); + + Body body = part.getBody(); + if (body instanceof Multipart) { + Multipart multipart = (Multipart) body; + if (part.getMimeType().equalsIgnoreCase("multipart/alternative")) { + /* + * For multipart/alternative parts we try to find a text/plain and a text/html + * child. Everything else we find is put into 'attachments'. + */ + List text = findTextPart(multipart, true); + + Set knownTextParts = getParts(text); + List html = findHtmlPart(multipart, knownTextParts, attachments, true); + + if (!text.isEmpty() || !html.isEmpty()) { + Alternative alternative = new Alternative(text, html); + viewables.add(alternative); + } + } else { + // For all other multipart parts we recurse to grab all viewable children. + for (Part bodyPart : multipart.getBodyParts()) { + viewables.addAll(getViewables(bodyPart, attachments)); + } + } + } else if (body instanceof Message && + !("attachment".equalsIgnoreCase(part.getContentDisposition()))) { + /* + * We only care about message/rfc822 parts whose Content-Disposition header has a value + * other than "attachment". + */ + Message message = (Message) body; + + // We add the Message object so we can extract the filename later. + viewables.add(new Viewable.MessageHeader(part, message)); + + // Recurse to grab all viewable parts and attachments from that message. + viewables.addAll(getViewables(message, attachments)); + } else if (isPartTextualBody(part)) { + /* + * Save text/plain and text/html + */ + String mimeType = part.getMimeType(); + if (mimeType.equalsIgnoreCase("text/plain")) { + Viewable.Text text = new Viewable.Text(part); + viewables.add(text); + } else { + Viewable.Html html = new Viewable.Html(part); + viewables.add(html); + } + } else { + // Everything else is treated as attachment. + attachments.add(part); + } + + return viewables; + } + + public static Set getTextParts(Part part) throws MessagingException { + List attachments = new ArrayList(); + return getParts(getViewables(part, attachments)); + } + + private static Message getMessageFromPart(Part part) { + while (part != null) { + if (part instanceof Message) + return (Message)part; + + if (!(part instanceof BodyPart)) + return null; + + Multipart multipart = ((BodyPart)part).getParent(); + if (multipart == null) + return null; + + part = multipart.getParent(); + } + return null; + } + + /** + * Search the children of a {@link Multipart} for {@code text/plain} parts. + * + * @param multipart The {@code Multipart} to search through. + * @param directChild If {@code true}, this method will return after the first {@code text/plain} was + * found. + * + * @return A list of {@link Viewable.Text} viewables. + * + * @throws MessagingException + * In case of an error. + */ + private static List findTextPart(Multipart multipart, boolean directChild) + throws MessagingException { + List viewables = new ArrayList(); + + for (Part part : multipart.getBodyParts()) { + Body body = part.getBody(); + if (body instanceof Multipart) { + Multipart innerMultipart = (Multipart) body; + + /* + * Recurse to find text parts. Since this is a multipart that is a child of a + * multipart/alternative we don't want to stop after the first text/plain part + * we find. This will allow to get all text parts for constructions like this: + * + * 1. multipart/alternative + * 1.1. multipart/mixed + * 1.1.1. text/plain + * 1.1.2. text/plain + * 1.2. text/html + */ + List textViewables = findTextPart(innerMultipart, false); + + if (!textViewables.isEmpty()) { + viewables.addAll(textViewables); + if (directChild) { + break; + } + } + } else if (isPartTextualBody(part) && part.getMimeType().equalsIgnoreCase("text/plain")) { + Viewable.Text text = new Viewable.Text(part); + viewables.add(text); + if (directChild) { + break; + } + } + } + return viewables; + } + + /** + * Search the children of a {@link Multipart} for {@code text/html} parts. + * Every part that is not a {@code text/html} we want to display, we add to 'attachments'. + * + * @param multipart The {@code Multipart} to search through. + * @param knownTextParts A set of {@code text/plain} parts that shouldn't be added to 'attachments'. + * @param attachments A list that will receive the parts that are considered attachments. + * @param directChild If {@code true}, this method will add all {@code text/html} parts except the first + * found to 'attachments'. + * + * @return A list of {@link Viewable.Text} viewables. + * + * @throws MessagingException In case of an error. + */ + private static List findHtmlPart(Multipart multipart, Set knownTextParts, + List attachments, boolean directChild) throws MessagingException { + List viewables = new ArrayList(); + + boolean partFound = false; + for (Part part : multipart.getBodyParts()) { + Body body = part.getBody(); + if (body instanceof Multipart) { + Multipart innerMultipart = (Multipart) body; + + if (directChild && partFound) { + // We already found our text/html part. Now we're only looking for attachments. + findAttachments(innerMultipart, knownTextParts, attachments); + } else { + /* + * Recurse to find HTML parts. Since this is a multipart that is a child of a + * multipart/alternative we don't want to stop after the first text/html part + * we find. This will allow to get all text parts for constructions like this: + * + * 1. multipart/alternative + * 1.1. text/plain + * 1.2. multipart/mixed + * 1.2.1. text/html + * 1.2.2. text/html + * 1.3. image/jpeg + */ + List htmlViewables = findHtmlPart(innerMultipart, knownTextParts, + attachments, false); + + if (!htmlViewables.isEmpty()) { + partFound = true; + viewables.addAll(htmlViewables); + } + } + } else if (!(directChild && partFound) && isPartTextualBody(part) && + part.getMimeType().equalsIgnoreCase("text/html")) { + Viewable.Html html = new Viewable.Html(part); + viewables.add(html); + partFound = true; + } else if (!knownTextParts.contains(part)) { + // Only add this part as attachment if it's not a viewable text/plain part found + // earlier. + attachments.add(part); + } + } + + return viewables; + } + + /** + * Traverse the MIME tree and add everything that's not a known text part to 'attachments'. + * + * @param multipart + * The {@link Multipart} to start from. + * @param knownTextParts + * A set of known text parts we don't want to end up in 'attachments'. + * @param attachments + * A list that will receive the parts that are considered attachments. + */ + private static void findAttachments(Multipart multipart, Set knownTextParts, + List attachments) { + for (Part part : multipart.getBodyParts()) { + Body body = part.getBody(); + if (body instanceof Multipart) { + Multipart innerMultipart = (Multipart) body; + findAttachments(innerMultipart, knownTextParts, attachments); + } else if (!knownTextParts.contains(part)) { + attachments.add(part); + } + } + } + + /** + * Build a set of message parts for fast lookups. + * + * @param viewables + * A list of {@link Viewable}s containing references to the message parts to include in + * the set. + * + * @return The set of viewable {@code Part}s. + * + * @see MimeUtility#findHtmlPart(Multipart, Set, List, boolean) + * @see MimeUtility#findAttachments(Multipart, Set, List) + */ + private static Set getParts(List viewables) { + Set parts = new HashSet(); + + for (Viewable viewable : viewables) { + if (viewable instanceof Textual) { + parts.add(((Textual) viewable).getPart()); + } else if (viewable instanceof Alternative) { + Alternative alternative = (Alternative) viewable; + parts.addAll(getParts(alternative.getText())); + parts.addAll(getParts(alternative.getHtml())); + } + } + + return parts; + } + + private static Boolean isPartTextualBody(Part part) throws MessagingException { + String disposition = part.getDisposition(); + String dispositionType = null; + String dispositionFilename = null; + if (disposition != null) { + dispositionType = MimeUtility.getHeaderParameter(disposition, null); + dispositionFilename = MimeUtility.getHeaderParameter(disposition, "filename"); + } + + /* + * A best guess that this part is intended to be an attachment and not inline. + */ + boolean attachment = ("attachment".equalsIgnoreCase(dispositionType) || (dispositionFilename != null)); + + if ((!attachment) && (part.getMimeType().equalsIgnoreCase("text/html"))) { + return true; + } + /* + * If the part is plain text and it got this far it's part of a + * mixed (et al) and should be rendered inline. + */ + else if ((!attachment) && (part.getMimeType().equalsIgnoreCase("text/plain"))) { + return true; + } + /* + * Finally, if it's nothing else we will include it as an attachment. + */ + else { + return false; + } + } +} diff --git a/src/com/fsck/k9/mail/internet/MimeMessage.java b/src/com/fsck/k9/mail/internet/MimeMessage.java index 02c028edf..c58a46080 100644 --- a/src/com/fsck/k9/mail/internet/MimeMessage.java +++ b/src/com/fsck/k9/mail/internet/MimeMessage.java @@ -473,7 +473,7 @@ public class MimeMessage extends Message { if (mBody instanceof Multipart) { ((Multipart)mBody).setCharset(charset); } else if (mBody instanceof TextBody) { - MimeUtility.setCharset(charset, this); + CharsetSupport.setCharset(charset, this); ((TextBody)mBody).setCharset(charset); } } diff --git a/src/com/fsck/k9/mail/internet/MimeUtility.java b/src/com/fsck/k9/mail/internet/MimeUtility.java index 6aaa93256..e56476878 100644 --- a/src/com/fsck/k9/mail/internet/MimeUtility.java +++ b/src/com/fsck/k9/mail/internet/MimeUtility.java @@ -1,13 +1,12 @@ package com.fsck.k9.mail.internet; -import android.content.Context; -import android.util.Log; -import com.fsck.k9.K9; -import com.fsck.k9.R; -import com.fsck.k9.mail.*; -import com.fsck.k9.mail.Message.RecipientType; -import com.fsck.k9.mail.internet.BinaryTempFileBody.BinaryTempFileBodyInputStream; +import com.fsck.k9.mail.Body; +import com.fsck.k9.mail.BodyPart; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.Multipart; +import com.fsck.k9.mail.Part; import org.apache.commons.io.IOUtils; import org.apache.james.mime4j.codec.Base64InputStream; @@ -17,32 +16,20 @@ import org.apache.james.mime4j.util.MimeUtil; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashSet; -import java.util.List; import java.util.Locale; -import java.util.Set; -import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.nio.charset.Charset; -import java.nio.charset.IllegalCharsetNameException; public class MimeUtility { public static final String DEFAULT_ATTACHMENT_MIME_TYPE = "application/octet-stream"; - public static final String K9_SETTINGS_MIME_TYPE = "application/x-k9settings"; - private static final String TEXT_DIVIDER = - "------------------------------------------------------------------------"; - /* * http://www.w3schools.com/media/media_mimeref.asp * + * http://www.stdicon.com/mimetypes */ - public static final String[][] MIME_TYPE_BY_EXTENSION_MAP = new String[][] { + private static final String[][] MIME_TYPE_BY_EXTENSION_MAP = new String[][] { //* Do not delete the next two lines { "", DEFAULT_ATTACHMENT_MIME_TYPE }, { "k9s", K9_SETTINGS_MIME_TYPE}, @@ -895,29 +882,6 @@ public class MimeUtility { { "zmm", "application/vnd.handheld-entertainment+xml"} }; - /** - * Table for MIME type replacements. - * - * Table format: wrong type, correct type - */ - private static final String[][] MIME_TYPE_REPLACEMENT_MAP = new String[][] { - {"image/jpg", "image/jpeg"}, - {"image/pjpeg", "image/jpeg"}, // see issue 1712 - {"application/x-zip-compressed", "application/zip"} // see issue 3791 - }; - - /** - * Table for character set fall-back. - * - * Table format: unsupported charset (regular expression), fall-back charset - */ - private static final String[][] CHARSET_FALLBACK_MAP = new String[][] { - // Some Android versions don't support KOI8-U - {"koi8-u", "koi8-r"}, - {"iso-2022-jp-[\\d]+", "iso-2022-jp"}, - // Default fall-back is US-ASCII - {".*", "US-ASCII"} - }; public static String unfold(String s) { if (s == null) { @@ -929,9 +893,9 @@ public class MimeUtility { private static String decode(String s, Message message) { if (s == null) { return null; + } else { + return DecoderUtil.decodeEncodedWords(s, message); } - - return DecoderUtil.decodeEncodedWords(s, message); } public static String unfoldAndDecode(String s) { @@ -951,24 +915,24 @@ public class MimeUtility { * Returns the named parameter of a header field. If name is null the first * parameter is returned, or if there are no additional parameters in the * field the entire field is returned. Otherwise the named parameter is - * searched for in a case insensitive fashion and returned. If the parameter - * cannot be found the method returns null. + * searched for in a case insensitive fashion and returned. * - * @param header - * @param name - * @return + * @param headerValue the header value + * @param parameterName the parameter name + * @return the value. if the parameter cannot be found the method returns null. */ - public static String getHeaderParameter(String header, String name) { - if (header == null) { + public static String getHeaderParameter(String headerValue, String parameterName) { + if (headerValue == null) { return null; } - header = header.replaceAll("\r|\n", ""); - String[] parts = header.split(";"); - if (name == null && parts.length > 0) { + headerValue = headerValue.replaceAll("\r|\n", ""); + String[] parts = headerValue.split(";"); + if (parameterName == null && parts.length > 0) { return parts[0].trim(); } for (String part : parts) { - if (part.trim().toLowerCase(Locale.US).startsWith(name.toLowerCase(Locale.US))) { + if (parameterName != null && + part.trim().toLowerCase(Locale.US).startsWith(parameterName.toLowerCase(Locale.US))) { String[] partParts = part.split("=", 2); if (partParts.length == 2) { String parameter = partParts[1].trim(); @@ -984,12 +948,11 @@ public class MimeUtility { return null; } - public static Part findFirstPartByMimeType(Part part, String mimeType) - throws MessagingException { + public static Part findFirstPartByMimeType(Part part, String mimeType) throws MessagingException { if (part.getBody() instanceof Multipart) { Multipart multipart = (Multipart)part.getBody(); for (BodyPart bodyPart : multipart.getBodyParts()) { - Part ret = findFirstPartByMimeType(bodyPart, mimeType); + Part ret = bodyPart.findFirstPartByMimeType(mimeType); if (ret != null) { return ret; } @@ -1000,104 +963,6 @@ public class MimeUtility { return null; } - /** - * Reads the Part's body and returns a String based on any charset conversion that needed - * to be done. Note, this does not return a text representation of HTML. - * @param part The part containing a body - * @return a String containing the converted text in the body, or null if there was no text - * or an error during conversion. - */ - public static String getTextFromPart(Part part) { - try { - if ((part != null) && (part.getBody() != null)) { - final Body body = part.getBody(); - if (body instanceof TextBody) { - return ((TextBody)body).getText(); - } - - final String mimeType = part.getMimeType(); - if ((mimeType != null) && MimeUtility.mimeTypeMatches(mimeType, "text/*")) { - /* - * We've got a text part, so let's see if it needs to be processed further. - */ - String charset = getHeaderParameter(part.getContentType(), "charset"); - /* - * determine the charset from HTML message. - */ - if (mimeType.equalsIgnoreCase("text/html") && charset == null) { - InputStream in = part.getBody().getInputStream(); - try { - byte[] buf = new byte[256]; - in.read(buf, 0, buf.length); - String str = new String(buf, "US-ASCII"); - - if (str.isEmpty()) { - return ""; - } - Pattern p = Pattern.compile("", Pattern.CASE_INSENSITIVE); - Matcher m = p.matcher(str); - if (m.find()) { - charset = m.group(1); - } - } finally { - try { - if (in instanceof BinaryTempFileBodyInputStream) { - /* - * If this is a BinaryTempFileBodyInputStream, calling close() - * will delete the file. But we can't let that happen because - * the file needs to be opened again by the code a few lines - * down. - */ - ((BinaryTempFileBodyInputStream) in).closeWithoutDeleting(); - } else { - in.close(); - } - } catch (Exception e) { /* ignore */ } - } - } - charset = fixupCharset(charset, getMessageFromPart(part)); - - /* - * Now we read the part into a buffer for further processing. Because - * the stream is now wrapped we'll remove any transfer encoding at this point. - */ - InputStream in = part.getBody().getInputStream(); - try { - String text = readToString(in, charset); - - // Replace the body with a TextBody that already contains the decoded text - part.setBody(new TextBody(text)); - - return text; - } finally { - try { - /* - * This time we don't care if it's a BinaryTempFileBodyInputStream. We - * replaced the body with a TextBody instance and hence don't need the - * file anymore. - */ - in.close(); - } catch (IOException e) { /* Ignore */ } - } - } - } - - } catch (OutOfMemoryError oom) { - /* - * If we are not able to process the body there's nothing we can do about it. Return - * null and let the upper layers handle the missing content. - */ - Log.e(K9.LOG_TAG, "Unable to getTextFromPart " + oom.toString()); - } catch (Exception e) { - /* - * If we are not able to process the body there's nothing we can do about it. Return - * null and let the upper layers handle the missing content. - */ - Log.e(K9.LOG_TAG, "Unable to getTextFromPart", e); - } - return null; - } - /** * Returns true if the given mimeType matches the matchAgainst specification. * @param mimeType A MIME type to check. @@ -1150,900 +1015,6 @@ public class MimeUtility { return tempBody; } - - /** - * Empty base class for the class hierarchy used by - * {@link MimeUtility#extractTextAndAttachments(Context, Message)}. - * - * @see Text - * @see Html - * @see MessageHeader - * @see Alternative - */ - static abstract class Viewable { /* empty */ } - - /** - * Class representing textual parts of a message that aren't marked as attachments. - * - * @see MimeUtility#isPartTextualBody(Part) - */ - static abstract class Textual extends Viewable { - private Part mPart; - - public Textual(Part part) { - mPart = part; - } - - public Part getPart() { - return mPart; - } - } - - /** - * Class representing a {@code text/plain} part of a message. - */ - static class Text extends Textual { - public Text(Part part) { - super(part); - } - } - - /** - * Class representing a {@code text/html} part of a message. - */ - static class Html extends Textual { - public Html(Part part) { - super(part); - } - } - - /** - * Class representing a {@code message/rfc822} part of a message. - * - *

    - * This is used to extract basic header information when the message contents are displayed - * inline. - *

    - */ - static class MessageHeader extends Viewable { - private Part mContainerPart; - private Message mMessage; - - public MessageHeader(Part containerPart, Message message) { - mContainerPart = containerPart; - mMessage = message; - } - - public Part getContainerPart() { - return mContainerPart; - } - - public Message getMessage() { - return mMessage; - } - } - - /** - * Class representing a {@code multipart/alternative} part of a message. - * - *

    - * Only relevant {@code text/plain} and {@code text/html} children are stored in this container - * class. - *

    - */ - static class Alternative extends Viewable { - private List mText; - private List mHtml; - - public Alternative(List text, List html) { - mText = text; - mHtml = html; - } - - public List getText() { - return mText; - } - - public List getHtml() { - return mHtml; - } - } - - /** - * Store viewable text of a message as plain text and HTML, and the parts considered - * attachments. - * - * @see MimeUtility#extractTextAndAttachments(Context, Message) - */ - public static class ViewableContainer { - /** - * The viewable text of the message in plain text. - */ - public final String text; - - /** - * The viewable text of the message in HTML. - */ - public final String html; - - /** - * The parts of the message considered attachments (everything not viewable). - */ - public final List attachments; - - ViewableContainer(String text, String html, List attachments) { - this.text = text; - this.html = html; - this.attachments = attachments; - } - } - - /** - * Collect attachment parts of a message. - * - * @param message - * The message to collect the attachment parts from. - * - * @return A list of parts regarded as attachments. - * - * @throws MessagingException - * In case of an error. - */ - public static List collectAttachments(Message message) - throws MessagingException { - try { - List attachments = new ArrayList(); - getViewables(message, attachments); - - return attachments; - } catch (Exception e) { - throw new MessagingException("Couldn't collect attachment parts", e); - } - } - - /** - * Collect the viewable textual parts of a message. - * - * @param message - * The message to extract the viewable parts from. - * - * @return A set of viewable parts of the message. - * - * @throws MessagingException - * In case of an error. - */ - public static Set collectTextParts(Message message) - throws MessagingException { - try { - List attachments = new ArrayList(); - - // Collect all viewable parts - List viewables = getViewables(message, attachments); - - // Extract the Part references - return getParts(viewables); - } catch (Exception e) { - throw new MessagingException("Couldn't extract viewable parts", e); - } - } - - /** - * Extract the viewable textual parts of a message and return the rest as attachments. - * - * @param context - * A {@link Context} instance that will be used to get localized strings. - * @param message - * The message to extract the text and attachments from. - * - * @return A {@link ViewableContainer} instance containing the textual parts of the message as - * plain text and HTML, and a list of message parts considered attachments. - * - * @throws MessagingException - * In case of an error. - */ - public static ViewableContainer extractTextAndAttachments(Context context, Message message) - throws MessagingException { - try { - List attachments = new ArrayList(); - - // Collect all viewable parts - List viewables = getViewables(message, attachments); - - /* - * Convert the tree of viewable parts into text and HTML - */ - - // Used to suppress the divider for the first viewable part - boolean hideDivider = true; - - StringBuilder text = new StringBuilder(); - StringBuilder html = new StringBuilder(); - - for (Viewable viewable : viewables) { - if (viewable instanceof Textual) { - // This is either a text/plain or text/html part. Fill the variables 'text' and - // 'html', converting between plain text and HTML as necessary. - text.append(buildText(viewable, !hideDivider)); - html.append(buildHtml(viewable, !hideDivider)); - hideDivider = false; - } else if (viewable instanceof MessageHeader) { - MessageHeader header = (MessageHeader) viewable; - Part containerPart = header.getContainerPart(); - Message innerMessage = header.getMessage(); - - addTextDivider(text, containerPart, !hideDivider); - addMessageHeaderText(context, text, innerMessage); - - addHtmlDivider(html, containerPart, !hideDivider); - addMessageHeaderHtml(context, html, innerMessage); - - hideDivider = true; - } else if (viewable instanceof Alternative) { - // Handle multipart/alternative contents - Alternative alternative = (Alternative) viewable; - - /* - * We made sure at least one of text/plain or text/html is present when - * creating the Alternative object. If one part is not present we convert the - * other one to make sure 'text' and 'html' always contain the same text. - */ - List textAlternative = alternative.getText().isEmpty() ? - alternative.getHtml() : alternative.getText(); - List htmlAlternative = alternative.getHtml().isEmpty() ? - alternative.getText() : alternative.getHtml(); - - // Fill the 'text' variable - boolean divider = !hideDivider; - for (Viewable textViewable : textAlternative) { - text.append(buildText(textViewable, divider)); - divider = true; - } - - // Fill the 'html' variable - divider = !hideDivider; - for (Viewable htmlViewable : htmlAlternative) { - html.append(buildHtml(htmlViewable, divider)); - divider = true; - } - hideDivider = false; - } - } - - return new ViewableContainer(text.toString(), html.toString(), attachments); - } catch (Exception e) { - throw new MessagingException("Couldn't extract viewable parts", e); - } - } - - /** - * Traverse the MIME tree of a message an extract viewable parts. - * - * @param part - * The message part to start from. - * @param attachments - * A list that will receive the parts that are considered attachments. - * - * @return A list of {@link Viewable}s. - * - * @throws MessagingException - * In case of an error. - */ - private static List getViewables(Part part, List attachments) throws MessagingException { - List viewables = new ArrayList(); - - Body body = part.getBody(); - if (body instanceof Multipart) { - Multipart multipart = (Multipart) body; - if (part.getMimeType().equalsIgnoreCase("multipart/alternative")) { - /* - * For multipart/alternative parts we try to find a text/plain and a text/html - * child. Everything else we find is put into 'attachments'. - */ - List text = findTextPart(multipart, true); - - Set knownTextParts = getParts(text); - List html = findHtmlPart(multipart, knownTextParts, attachments, true); - - if (!text.isEmpty() || !html.isEmpty()) { - Alternative alternative = new Alternative(text, html); - viewables.add(alternative); - } - } else { - // For all other multipart parts we recurse to grab all viewable children. - for (Part bodyPart : multipart.getBodyParts()) { - viewables.addAll(getViewables(bodyPart, attachments)); - } - } - } else if (body instanceof Message && - !("attachment".equalsIgnoreCase(getContentDisposition(part)))) { - /* - * We only care about message/rfc822 parts whose Content-Disposition header has a value - * other than "attachment". - */ - Message message = (Message) body; - - // We add the Message object so we can extract the filename later. - viewables.add(new MessageHeader(part, message)); - - // Recurse to grab all viewable parts and attachments from that message. - viewables.addAll(getViewables(message, attachments)); - } else if (isPartTextualBody(part)) { - /* - * Save text/plain and text/html - */ - String mimeType = part.getMimeType(); - if (mimeType.equalsIgnoreCase("text/plain")) { - Text text = new Text(part); - viewables.add(text); - } else { - Html html = new Html(part); - viewables.add(html); - } - } else { - // Everything else is treated as attachment. - attachments.add(part); - } - - return viewables; - } - - /** - * Search the children of a {@link Multipart} for {@code text/plain} parts. - * - * @param multipart - * The {@code Multipart} to search through. - * @param directChild - * If {@code true}, this method will return after the first {@code text/plain} was - * found. - * - * @return A list of {@link Text} viewables. - * - * @throws MessagingException - * In case of an error. - */ - private static List findTextPart(Multipart multipart, boolean directChild) - throws MessagingException { - List viewables = new ArrayList(); - - for (Part part : multipart.getBodyParts()) { - Body body = part.getBody(); - if (body instanceof Multipart) { - Multipart innerMultipart = (Multipart) body; - - /* - * Recurse to find text parts. Since this is a multipart that is a child of a - * multipart/alternative we don't want to stop after the first text/plain part - * we find. This will allow to get all text parts for constructions like this: - * - * 1. multipart/alternative - * 1.1. multipart/mixed - * 1.1.1. text/plain - * 1.1.2. text/plain - * 1.2. text/html - */ - List textViewables = findTextPart(innerMultipart, false); - - if (!textViewables.isEmpty()) { - viewables.addAll(textViewables); - if (directChild) { - break; - } - } - } else if (isPartTextualBody(part) && part.getMimeType().equalsIgnoreCase("text/plain")) { - Text text = new Text(part); - viewables.add(text); - if (directChild) { - break; - } - } - } - - return viewables; - } - - /** - * Search the children of a {@link Multipart} for {@code text/html} parts. - * - *

    - * Every part that is not a {@code text/html} we want to display, we add to 'attachments'. - *

    - * - * @param multipart - * The {@code Multipart} to search through. - * @param knownTextParts - * A set of {@code text/plain} parts that shouldn't be added to 'attachments'. - * @param attachments - * A list that will receive the parts that are considered attachments. - * @param directChild - * If {@code true}, this method will add all {@code text/html} parts except the first - * found to 'attachments'. - * - * @return A list of {@link Text} viewables. - * - * @throws MessagingException - * In case of an error. - */ - private static List findHtmlPart(Multipart multipart, Set knownTextParts, - List attachments, boolean directChild) throws MessagingException { - List viewables = new ArrayList(); - - boolean partFound = false; - for (Part part : multipart.getBodyParts()) { - Body body = part.getBody(); - if (body instanceof Multipart) { - Multipart innerMultipart = (Multipart) body; - - if (directChild && partFound) { - // We already found our text/html part. Now we're only looking for attachments. - findAttachments(innerMultipart, knownTextParts, attachments); - } else { - /* - * Recurse to find HTML parts. Since this is a multipart that is a child of a - * multipart/alternative we don't want to stop after the first text/html part - * we find. This will allow to get all text parts for constructions like this: - * - * 1. multipart/alternative - * 1.1. text/plain - * 1.2. multipart/mixed - * 1.2.1. text/html - * 1.2.2. text/html - * 1.3. image/jpeg - */ - List htmlViewables = findHtmlPart(innerMultipart, knownTextParts, - attachments, false); - - if (!htmlViewables.isEmpty()) { - partFound = true; - viewables.addAll(htmlViewables); - } - } - } else if (!(directChild && partFound) && isPartTextualBody(part) && - part.getMimeType().equalsIgnoreCase("text/html")) { - Html html = new Html(part); - viewables.add(html); - partFound = true; - } else if (!knownTextParts.contains(part)) { - // Only add this part as attachment if it's not a viewable text/plain part found - // earlier. - attachments.add(part); - } - } - - return viewables; - } - - /** - * Build a set of message parts for fast lookups. - * - * @param viewables - * A list of {@link Viewable}s containing references to the message parts to include in - * the set. - * - * @return The set of viewable {@code Part}s. - * - * @see MimeUtility#findHtmlPart(Multipart, Set, List, boolean) - * @see MimeUtility#findAttachments(Multipart, Set, List) - */ - private static Set getParts(List viewables) { - Set parts = new HashSet(); - - for (Viewable viewable : viewables) { - if (viewable instanceof Textual) { - parts.add(((Textual) viewable).getPart()); - } else if (viewable instanceof Alternative) { - Alternative alternative = (Alternative) viewable; - parts.addAll(getParts(alternative.getText())); - parts.addAll(getParts(alternative.getHtml())); - } - } - - return parts; - } - - /** - * Traverse the MIME tree and add everything that's not a known text part to 'attachments'. - * - * @param multipart - * The {@link Multipart} to start from. - * @param knownTextParts - * A set of known text parts we don't want to end up in 'attachments'. - * @param attachments - * A list that will receive the parts that are considered attachments. - */ - private static void findAttachments(Multipart multipart, Set knownTextParts, - List attachments) { - for (Part part : multipart.getBodyParts()) { - Body body = part.getBody(); - if (body instanceof Multipart) { - Multipart innerMultipart = (Multipart) body; - findAttachments(innerMultipart, knownTextParts, attachments); - } else if (!knownTextParts.contains(part)) { - attachments.add(part); - } - } - } - - /** - * Extract important header values from a message to display inline (plain text version). - * - * @param context - * A {@link Context} instance that will be used to get localized strings. - * @param text - * The {@link StringBuilder} that will receive the (plain text) output. - * @param message - * The message to extract the header values from. - * - * @throws MessagingException - * In case of an error. - */ - private static void addMessageHeaderText(Context context, StringBuilder text, Message message) - throws MessagingException { - // From: - Address[] from = message.getFrom(); - if (from != null && from.length > 0) { - text.append(context.getString(R.string.message_compose_quote_header_from)); - text.append(' '); - text.append(Address.toString(from)); - text.append("\r\n"); - } - - // To: - Address[] to = message.getRecipients(RecipientType.TO); - if (to != null && to.length > 0) { - text.append(context.getString(R.string.message_compose_quote_header_to)); - text.append(' '); - text.append(Address.toString(to)); - text.append("\r\n"); - } - - // Cc: - Address[] cc = message.getRecipients(RecipientType.CC); - if (cc != null && cc.length > 0) { - text.append(context.getString(R.string.message_compose_quote_header_cc)); - text.append(' '); - text.append(Address.toString(cc)); - text.append("\r\n"); - } - - // Date: - Date date = message.getSentDate(); - if (date != null) { - text.append(context.getString(R.string.message_compose_quote_header_send_date)); - text.append(' '); - text.append(date.toString()); - text.append("\r\n"); - } - - // Subject: - String subject = message.getSubject(); - text.append(context.getString(R.string.message_compose_quote_header_subject)); - text.append(' '); - if (subject == null) { - text.append(context.getString(R.string.general_no_subject)); - } else { - text.append(subject); - } - text.append("\r\n\r\n"); - } - - /** - * Extract important header values from a message to display inline (HTML version). - * - * @param context - * A {@link Context} instance that will be used to get localized strings. - * @param html - * The {@link StringBuilder} that will receive the (HTML) output. - * @param message - * The message to extract the header values from. - * - * @throws MessagingException - * In case of an error. - */ - private static void addMessageHeaderHtml(Context context, StringBuilder html, Message message) - throws MessagingException { - - html.append(""); - - // From: - Address[] from = message.getFrom(); - if (from != null && from.length > 0) { - addTableRow(html, context.getString(R.string.message_compose_quote_header_from), - Address.toString(from)); - } - - // To: - Address[] to = message.getRecipients(RecipientType.TO); - if (to != null && to.length > 0) { - addTableRow(html, context.getString(R.string.message_compose_quote_header_to), - Address.toString(to)); - } - - // Cc: - Address[] cc = message.getRecipients(RecipientType.CC); - if (cc != null && cc.length > 0) { - addTableRow(html, context.getString(R.string.message_compose_quote_header_cc), - Address.toString(cc)); - } - - // Date: - Date date = message.getSentDate(); - if (date != null) { - addTableRow(html, context.getString(R.string.message_compose_quote_header_send_date), - date.toString()); - } - - // Subject: - String subject = message.getSubject(); - addTableRow(html, context.getString(R.string.message_compose_quote_header_subject), - (subject == null) ? context.getString(R.string.general_no_subject) : subject); - - html.append("
    "); - } - - /** - * Output an HTML table two column row with some hardcoded style. - * - * @param html - * The {@link StringBuilder} that will receive the output. - * @param header - * The string to be put in the {@code TH} element. - * @param value - * The string to be put in the {@code TD} element. - */ - private static void addTableRow(StringBuilder html, String header, String value) { - html.append(""); - html.append(header); - html.append(""); - html.append(""); - html.append(value); - html.append(""); - } - - /** - * Use the contents of a {@link Viewable} to create the plain text to be displayed. - * - *

    - * This will use {@link HtmlConverter#htmlToText(String)} to convert HTML parts to plain text - * if necessary. - *

    - * - * @param viewable - * The viewable part to build the text from. - * @param prependDivider - * {@code true}, if the text divider should be inserted as first element. - * {@code false}, otherwise. - * - * @return The contents of the supplied viewable instance as plain text. - */ - private static StringBuilder buildText(Viewable viewable, boolean prependDivider) - { - StringBuilder text = new StringBuilder(); - if (viewable instanceof Textual) { - Part part = ((Textual)viewable).getPart(); - addTextDivider(text, part, prependDivider); - - String t = getTextFromPart(part); - if (t == null) { - t = ""; - } else if (viewable instanceof Html) { - t = HtmlConverter.htmlToText(t); - } - text.append(t); - } else if (viewable instanceof Alternative) { - // That's odd - an Alternative as child of an Alternative; go ahead and try to use the - // text/plain child; fall-back to the text/html part. - Alternative alternative = (Alternative) viewable; - - List textAlternative = alternative.getText().isEmpty() ? - alternative.getHtml() : alternative.getText(); - - boolean divider = prependDivider; - for (Viewable textViewable : textAlternative) { - text.append(buildText(textViewable, divider)); - divider = true; - } - } - - return text; - } - - /* - * Some constants that are used by addTextDivider() below. - */ - private static final int TEXT_DIVIDER_LENGTH = TEXT_DIVIDER.length(); - private static final String FILENAME_PREFIX = "----- "; - private static final int FILENAME_PREFIX_LENGTH = FILENAME_PREFIX.length(); - private static final String FILENAME_SUFFIX = " "; - private static final int FILENAME_SUFFIX_LENGTH = FILENAME_SUFFIX.length(); - - /** - * Add a plain text divider between two plain text message parts. - * - * @param text - * The {@link StringBuilder} to append the divider to. - * @param part - * The message part that will follow after the divider. This is used to extract the - * part's name. - * @param prependDivider - * {@code true}, if the divider should be appended. {@code false}, otherwise. - */ - private static void addTextDivider(StringBuilder text, Part part, boolean prependDivider) { - if (prependDivider) { - String filename = getPartName(part); - - text.append("\r\n\r\n"); - int len = filename.length(); - if (len > 0) { - if (len > TEXT_DIVIDER_LENGTH - FILENAME_PREFIX_LENGTH - FILENAME_SUFFIX_LENGTH) { - filename = filename.substring(0, TEXT_DIVIDER_LENGTH - FILENAME_PREFIX_LENGTH - - FILENAME_SUFFIX_LENGTH - 3) + "..."; - } - text.append(FILENAME_PREFIX); - text.append(filename); - text.append(FILENAME_SUFFIX); - text.append(TEXT_DIVIDER.substring(0, TEXT_DIVIDER_LENGTH - - FILENAME_PREFIX_LENGTH - filename.length() - FILENAME_SUFFIX_LENGTH)); - } else { - text.append(TEXT_DIVIDER); - } - text.append("\r\n\r\n"); - } - } - - /** - * Use the contents of a {@link Viewable} to create the HTML to be displayed. - * - *

    - * This will use {@link HtmlConverter#textToHtml(String)} to convert plain text parts - * to HTML if necessary. - *

    - * - * @param viewable - * The viewable part to build the HTML from. - * @param prependDivider - * {@code true}, if the HTML divider should be inserted as first element. - * {@code false}, otherwise. - * - * @return The contents of the supplied viewable instance as HTML. - */ - private static StringBuilder buildHtml(Viewable viewable, boolean prependDivider) - { - StringBuilder html = new StringBuilder(); - if (viewable instanceof Textual) { - Part part = ((Textual)viewable).getPart(); - addHtmlDivider(html, part, prependDivider); - - String t = getTextFromPart(part); - if (t == null) { - t = ""; - } else if (viewable instanceof Text) { - t = HtmlConverter.textToHtml(t); - } - html.append(t); - } else if (viewable instanceof Alternative) { - // That's odd - an Alternative as child of an Alternative; go ahead and try to use the - // text/html child; fall-back to the text/plain part. - Alternative alternative = (Alternative) viewable; - - List htmlAlternative = alternative.getHtml().isEmpty() ? - alternative.getText() : alternative.getHtml(); - - boolean divider = prependDivider; - for (Viewable htmlViewable : htmlAlternative) { - html.append(buildHtml(htmlViewable, divider)); - divider = true; - } - } - - return html; - } - - /** - * Add an HTML divider between two HTML message parts. - * - * @param html - * The {@link StringBuilder} to append the divider to. - * @param part - * The message part that will follow after the divider. This is used to extract the - * part's name. - * @param prependDivider - * {@code true}, if the divider should be appended. {@code false}, otherwise. - */ - private static void addHtmlDivider(StringBuilder html, Part part, boolean prependDivider) { - if (prependDivider) { - String filename = getPartName(part); - - html.append("

    "); - html.append(filename); - html.append("

    "); - } - } - - /** - * Get the name of the message part. - * - * @param part - * The part to get the name for. - * - * @return The (file)name of the part if available. An empty string, otherwise. - */ - private static String getPartName(Part part) { - try { - String disposition = part.getDisposition(); - if (disposition != null) { - String name = MimeUtility.getHeaderParameter(disposition, "filename"); - return (name == null) ? "" : name; - } - } - catch (MessagingException e) { /* ignore */ } - - return ""; - } - - /** - * Get the value of the {@code Content-Disposition} header. - * - * @param part - * The message part to read the header from. - * - * @return The value of the {@code Content-Disposition} header if available. {@code null}, - * otherwise. - */ - private static String getContentDisposition(Part part) { - try { - String disposition = part.getDisposition(); - if (disposition != null) { - return MimeUtility.getHeaderParameter(disposition, null); - } - } - catch (MessagingException e) { /* ignore */ } - - return null; - } - - private static Boolean isPartTextualBody(Part part) throws MessagingException { - String disposition = part.getDisposition(); - String dispositionType = null; - String dispositionFilename = null; - if (disposition != null) { - dispositionType = MimeUtility.getHeaderParameter(disposition, null); - dispositionFilename = MimeUtility.getHeaderParameter(disposition, "filename"); - } - - /* - * A best guess that this part is intended to be an attachment and not inline. - */ - boolean attachment = ("attachment".equalsIgnoreCase(dispositionType) || (dispositionFilename != null)); - - if ((!attachment) && (part.getMimeType().equalsIgnoreCase("text/html"))) { - return true; - } - /* - * If the part is plain text and it got this far it's part of a - * mixed (et al) and should be rendered inline. - */ - else if ((!attachment) && (part.getMimeType().equalsIgnoreCase("text/plain"))) { - return true; - } - /* - * Finally, if it's nothing else we will include it as an attachment. - */ - else { - return false; - } - } - - public static String getCharsetFromAddress(String address) { - String variant = getJisVariantFromAddress(address); - if (variant != null) { - String charset = "x-" + variant + "-shift_jis-2007"; - if (Charset.isSupported(charset)) - return charset; - } - - return "UTF-8"; - } - public static String getMimeTypeByExtension(String filename) { String returnedType = null; String extension = null; @@ -2078,50 +1049,6 @@ public class MimeUtility { return null; } - /** - * Convert some wrong MIME types encountered in the wild to canonical MIME types. - * - * @param mimeType - * The original MIME type - * - * @return If {@code mimeType} is known to be wrong the correct MIME type is returned. - * Otherwise the lower case version of {@code mimeType} is returned. - * - * @see #MIME_TYPE_REPLACEMENT_MAP - */ - private static String canonicalizeMimeType(String mimeType) { - String lowerCaseMimeType = mimeType.toLowerCase(Locale.US); - for (String[] mimeTypeMapEntry : MIME_TYPE_REPLACEMENT_MAP) { - if (mimeTypeMapEntry[0].equals(lowerCaseMimeType)) { - return mimeTypeMapEntry[1]; - } - } - return lowerCaseMimeType; - } - - /** - * When viewing the attachment we want the MIME type to be as sensible as possible. So we fix - * it up if necessary. - * - * @param mimeType - * The original MIME type of the attachment. - * @param name - * The (file)name of the attachment. - * - * @return The best MIME type we can come up with. - */ - public static String getMimeTypeForViewing(String mimeType, String name) { - if (DEFAULT_ATTACHMENT_MIME_TYPE.equalsIgnoreCase(mimeType)) { - // If the MIME type is the generic "application/octet-stream" - // we try to find a better one by looking at the file extension. - return getMimeTypeByExtension(name); - } - - // Some messages contain wrong MIME types. See if we know better. - return canonicalizeMimeType(mimeType); - } - - /** * Get a default content-transfer-encoding for use with a given content-type * when adding an unencoded attachment. It's possible that 8bit encodings @@ -2152,1240 +1079,4 @@ public class MimeUtility { return (MimeUtil.ENC_BASE64); } } - - private static Message getMessageFromPart(Part part) { - while (part != null) { - if (part instanceof Message) - return (Message)part; - - if (!(part instanceof BodyPart)) - return null; - - Multipart multipart = ((BodyPart)part).getParent(); - if (multipart == null) - return null; - - part = multipart.getParent(); - } - return null; - } - - public static String fixupCharset(String charset, Message message) throws MessagingException { - if (charset == null || "0".equals(charset)) - charset = "US-ASCII"; // No encoding, so use us-ascii, which is the standard. - - charset = charset.toLowerCase(Locale.US); - if (charset.equals("cp932")) - charset = "shift_jis"; - - if (charset.equals("shift_jis") || charset.equals("iso-2022-jp")) { - String variant = getJisVariantFromMessage(message); - if (variant != null) - charset = "x-" + variant + "-" + charset + "-2007"; - } - return charset; - } - - private static String getJisVariantFromMessage(Message message) throws MessagingException { - if (message == null) - return null; - - // If a receiver is known to use a JIS variant, the sender transfers the message after converting the - // charset as a convention. - String variant = getJisVariantFromReceivedHeaders(message); - if (variant != null) - return variant; - - // If a receiver is not known to use any JIS variants, the sender transfers the message without converting - // the charset. - variant = getJisVariantFromFromHeaders(message); - if (variant != null) - return variant; - - return getJisVariantFromMailerHeaders(message); - } - - private static String getJisVariantFromReceivedHeaders(Message message) throws MessagingException { - String receivedHeaders[] = message.getHeader("Received"); - if (receivedHeaders == null) - return null; - - for (String receivedHeader : receivedHeaders) { - String address = getAddressFromReceivedHeader(receivedHeader); - if (address == null) - continue; - String variant = getJisVariantFromAddress(address); - if (variant != null) - return variant; - } - return null; - } - - private static String getAddressFromReceivedHeader(String receivedHeader) { - // Not implemented yet! Extract an address from the FOR clause of the given Received header. - return null; - } - - private static String getJisVariantFromFromHeaders(Message message) throws MessagingException { - Address addresses[] = message.getFrom(); - if (addresses == null || addresses.length == 0) - return null; - - return getJisVariantFromAddress(addresses[0].getAddress()); - } - - private static String getJisVariantFromAddress(String address) { - if (address == null) - return null; - if (isInDomain(address, "docomo.ne.jp") || isInDomain(address, "dwmail.jp") || - isInDomain(address, "pdx.ne.jp") || isInDomain(address, "willcom.com") || - isInDomain(address, "emnet.ne.jp") || isInDomain(address, "emobile.ne.jp")) - return "docomo"; - else if (isInDomain(address, "softbank.ne.jp") || isInDomain(address, "vodafone.ne.jp") || - isInDomain(address, "disney.ne.jp") || isInDomain(address, "vertuclub.ne.jp")) - return "softbank"; - else if (isInDomain(address, "ezweb.ne.jp") || isInDomain(address, "ido.ne.jp")) - return "kddi"; - return null; - } - - private static boolean isInDomain(String address, String domain) { - int index = address.length() - domain.length() - 1; - if (index < 0) - return false; - - char c = address.charAt(index); - if (c != '@' && c != '.') - return false; - - return address.endsWith(domain); - } - - private static String getJisVariantFromMailerHeaders(Message message) throws MessagingException { - String mailerHeaders[] = message.getHeader("X-Mailer"); - if (mailerHeaders == null || mailerHeaders.length == 0) - return null; - - if (mailerHeaders[0].startsWith("iPhone Mail ") || mailerHeaders[0].startsWith("iPad Mail ")) - return "iphone"; - - return null; - } - - public static String readToString(InputStream in, String charset) throws IOException { - boolean isIphoneString = false; - - // iso-2022-jp variants are supported by no versions as of Dec 2010. - if (charset.length() > 19 && charset.startsWith("x-") && - charset.endsWith("-iso-2022-jp-2007") && !Charset.isSupported(charset)) { - in = new Iso2022JpToShiftJisInputStream(in); - charset = "x-" + charset.substring(2, charset.length() - 17) + "-shift_jis-2007"; - } - - // shift_jis variants are supported by Eclair and later. - if (charset.length() > 17 && charset.startsWith("x-") && - charset.endsWith("-shift_jis-2007") && !Charset.isSupported(charset)) { - // If the JIS variant is iPhone, map the Unicode private use area in iPhone to the one in Android after - // converting the character set from the standard Shift JIS to Unicode. - if (charset.substring(2, charset.length() - 15).equals("iphone")) - isIphoneString = true; - - charset = "shift_jis"; - } - - /* - * See if there is conversion from the MIME charset to the Java one. - * this function may also throw an exception if the charset name is not known - */ - boolean supported; - try { - supported = Charset.isSupported(charset); - } catch (IllegalCharsetNameException e) { - supported = false; - } - - for (String[] rule: CHARSET_FALLBACK_MAP) { - if (supported) { - break; - } - - if (charset.matches(rule[0])) { - Log.e(K9.LOG_TAG, "I don't know how to deal with the charset " + charset + - ". Falling back to " + rule[1]); - charset = rule[1]; - try { - supported = Charset.isSupported(charset); - } catch (IllegalCharsetNameException e) { - supported = false; - } - } - } - - /* - * Convert and return as new String - */ - String str = IOUtils.toString(in, charset); - - if (isIphoneString) - str = importStringFromIphone(str); - return str; - } - - static private String importStringFromIphone(String str) { - StringBuilder buff = new StringBuilder(str.length()); - for (int i = 0; i < str.length(); i = str.offsetByCodePoints(i, 1)) { - int codePoint = str.codePointAt(i); - buff.appendCodePoint(importCodePointFromIphone(codePoint)); - } - return buff.toString(); - } - - static private int importCodePointFromIphone(int codePoint) { - switch (codePoint) { - case 0xE001: - return 0xFE19B; - case 0xE002: - return 0xFE19C; - case 0xE003: - return 0xFE823; - case 0xE004: - return 0xFE19D; - case 0xE005: - return 0xFE19E; - case 0xE006: - return 0xFE4CF; - case 0xE007: - return 0xFE4CD; - case 0xE008: - return 0xFE4EF; - case 0xE009: - return 0xFE523; - case 0xE00A: - return 0xFE525; - case 0xE00B: - return 0xFE528; - case 0xE00C: - return 0xFE538; - case 0xE00D: - return 0xFEB96; - case 0xE00E: - return 0xFEB97; - case 0xE00F: - return 0xFEB98; - case 0xE010: - return 0xFEB93; - case 0xE011: - return 0xFEB94; - case 0xE012: - return 0xFEB95; - case 0xE013: - return 0xFE7D5; - case 0xE014: - return 0xFE7D2; - case 0xE015: - return 0xFE7D3; - case 0xE016: - return 0xFE7D1; - case 0xE017: - return 0xFE7DA; - case 0xE018: - return 0xFE7D4; - case 0xE019: - return 0xFE1BD; - case 0xE01A: - return 0xFE1BE; - case 0xE01B: - return 0xFE7E4; - case 0xE01C: - return 0xFE7EA; - case 0xE01D: - return 0xFE7E9; - case 0xE01E: - return 0xFE7DF; - case 0xE01F: - return 0xFE7E3; - case 0xE020: - return 0xFEB09; - case 0xE021: - return 0xFEB04; - case 0xE022: - return 0xFEB0C; - case 0xE023: - return 0xFEB0E; - case 0xE024: - return 0xFE01E; - case 0xE025: - return 0xFE01F; - case 0xE026: - return 0xFE020; - case 0xE027: - return 0xFE021; - case 0xE028: - return 0xFE022; - case 0xE029: - return 0xFE023; - case 0xE02A: - return 0xFE024; - case 0xE02B: - return 0xFE025; - case 0xE02C: - return 0xFE026; - case 0xE02D: - return 0xFE027; - case 0xE02E: - return 0xFE028; - case 0xE02F: - return 0xFE029; - case 0xE030: - return 0xFE040; - case 0xE031: - return 0xFE4D2; - case 0xE032: - return 0xFE041; - case 0xE033: - return 0xFE512; - case 0xE034: - return 0xFE825; - case 0xE035: - return 0xFE826; - case 0xE036: - return 0xFE4B0; - case 0xE037: - return 0xFE4BB; - case 0xE038: - return 0xFE4B2; - case 0xE039: - return 0xFE7EC; - case 0xE03A: - return 0xFE7F5; - case 0xE03B: - return 0xFE4C3; - case 0xE03C: - return 0xFE800; - case 0xE03D: - return 0xFE801; - case 0xE03E: - return 0xFE813; - case 0xE03F: - return 0xFEB82; - case 0xE040: - return 0xFE815; - case 0xE041: - return 0xFE816; - case 0xE042: - return 0xFE818; - case 0xE043: - return 0xFE980; - case 0xE044: - return 0xFE982; - case 0xE045: - return 0xFE981; - case 0xE046: - return 0xFE962; - case 0xE047: - return 0xFE983; - case 0xE048: - return 0xFE003; - case 0xE049: - return 0xFE001; - case 0xE04A: - return 0xFE000; - case 0xE04B: - return 0xFE002; - case 0xE04C: - return 0xFE014; - case 0xE04D: - return 0xFE009; - case 0xE04E: - return 0xFE1AF; - case 0xE04F: - return 0xFE1B8; - case 0xE050: - return 0xFE1C0; - case 0xE051: - return 0xFE1C1; - case 0xE052: - return 0xFE1B7; - case 0xE053: - return 0xFE1C2; - case 0xE054: - return 0xFE1C3; - case 0xE055: - return 0xFE1BC; - case 0xE056: - return 0xFE335; - case 0xE057: - return 0xFE330; - case 0xE058: - return 0xFE323; - case 0xE059: - return 0xFE320; - case 0xE05A: - return 0xFE4F4; - case 0xE101: - return 0xFE52D; - case 0xE102: - return 0xFE52E; - case 0xE103: - return 0xFE52B; - case 0xE104: - return 0xFE526; - case 0xE105: - return 0xFE329; - case 0xE106: - return 0xFE327; - case 0xE107: - return 0xFE341; - case 0xE108: - return 0xFE344; - case 0xE109: - return 0xFE1C4; - case 0xE10A: - return 0xFE1C5; - case 0xE10B: - return 0xFE1BF; - case 0xE10C: - return 0xFE1B0; - case 0xE10D: - return 0xFE7ED; - case 0xE10E: - return 0xFE4D1; - case 0xE10F: - return 0xFEB56; - case 0xE110: - return 0xFE03C; - case 0xE111: - return 0xFE827; - case 0xE112: - return 0xFE510; - case 0xE113: - return 0xFE4F5; - case 0xE114: - return 0xFEB85; - case 0xE115: - return 0xFE7D9; - case 0xE116: - return 0xFE4CA; - case 0xE117: - return 0xFE515; - case 0xE118: - return 0xFE03F; - case 0xE119: - return 0xFE042; - case 0xE11A: - return 0xFE1B2; - case 0xE11B: - return 0xFE1AE; - case 0xE11C: - return 0xFE1B3; - case 0xE11D: - return 0xFE4F6; - case 0xE11E: - return 0xFE53B; - case 0xE11F: - return 0xFE537; - case 0xE120: - return 0xFE960; - case 0xE121: - return 0xFE4BC; - case 0xE122: - return 0xFE7FB; - case 0xE123: - return 0xFE7FA; - case 0xE124: - return 0xFE7FD; - case 0xE125: - return 0xFE807; - case 0xE126: - return 0xFE81D; - case 0xE127: - return 0xFE81E; - case 0xE128: - return 0xFE81F; - case 0xE129: - return 0xFE820; - case 0xE12A: - return 0xFE81C; - case 0xE12B: - return 0xFE1B1; - case 0xE12C: - return 0xFE81B; - case 0xE12D: - return 0xFE80B; - case 0xE12E: - return 0xFEB32; - case 0xE12F: - return 0xFE4DD; - case 0xE130: - return 0xFE80C; - case 0xE131: - return 0xFE7DB; - case 0xE132: - return 0xFE7D7; - case 0xE133: - return 0xFE80D; - case 0xE134: - return 0xFE7DC; - case 0xE135: - return 0xFE7EE; - case 0xE136: - return 0xFE7EB; - case 0xE137: - return 0xFE7F8; - case 0xE138: - return 0xFEB33; - case 0xE139: - return 0xFEB34; - case 0xE13A: - return 0xFEB35; - case 0xE13B: - return 0xFE509; - case 0xE13C: - return 0xFEB59; - case 0xE13D: - return 0xFE004; - case 0xE13E: - return 0xFE4D6; - case 0xE13F: - return 0xFE505; - case 0xE140: - return 0xFE507; - case 0xE141: - return 0xFE821; - case 0xE142: - return 0xFE52F; - case 0xE143: - return 0xFE514; - case 0xE144: - return 0xFEB86; - case 0xE145: - return 0xFEB87; - case 0xE146: - return 0xFE00B; - case 0xE147: - return 0xFE965; - case 0xE148: - return 0xFE546; - case 0xE149: - return 0xFE4DE; - case 0xE14A: - return 0xFE4DF; - case 0xE14B: - return 0xFE531; - case 0xE14C: - return 0xFEB5E; - case 0xE14D: - return 0xFE4B5; - case 0xE14E: - return 0xFE7F7; - case 0xE14F: - return 0xFE7F6; - case 0xE150: - return 0xFE7E7; - case 0xE151: - return 0xFE506; - case 0xE152: - return 0xFE1A1; - case 0xE153: - return 0xFE4B3; - case 0xE154: - return 0xFE4B6; - case 0xE155: - return 0xFE4B4; - case 0xE156: - return 0xFE4B9; - case 0xE157: - return 0xFE4BA; - case 0xE158: - return 0xFE4B7; - case 0xE159: - return 0xFE7E6; - case 0xE15A: - return 0xFE7EF; - case 0xE201: - return 0xFE7F0; - case 0xE202: - return 0xFE7E8; - case 0xE203: - return 0xFEB24; - case 0xE204: - return 0xFEB19; - case 0xE205: - return 0xFEB61; - case 0xE206: - return 0xFEB62; - case 0xE207: - return 0xFEB25; - case 0xE208: - return 0xFEB1F; - case 0xE209: - return 0xFE044; - case 0xE20A: - return 0xFEB20; - case 0xE20B: - return 0xFE838; - case 0xE20C: - return 0xFEB1A; - case 0xE20D: - return 0xFEB1C; - case 0xE20E: - return 0xFEB1B; - case 0xE20F: - return 0xFEB1D; - case 0xE210: - return 0xFE82C; - case 0xE211: - return 0xFE82B; - case 0xE212: - return 0xFEB36; - case 0xE213: - return 0xFEB37; - case 0xE214: - return 0xFEB38; - case 0xE215: - return 0xFEB39; - case 0xE216: - return 0xFEB3A; - case 0xE217: - return 0xFEB3B; - case 0xE218: - return 0xFEB3C; - case 0xE219: - return 0xFEB63; - case 0xE21A: - return 0xFEB64; - case 0xE21B: - return 0xFEB67; - case 0xE21C: - return 0xFE82E; - case 0xE21D: - return 0xFE82F; - case 0xE21E: - return 0xFE830; - case 0xE21F: - return 0xFE831; - case 0xE220: - return 0xFE832; - case 0xE221: - return 0xFE833; - case 0xE222: - return 0xFE834; - case 0xE223: - return 0xFE835; - case 0xE224: - return 0xFE836; - case 0xE225: - return 0xFE837; - case 0xE226: - return 0xFEB3D; - case 0xE227: - return 0xFEB3E; - case 0xE228: - return 0xFEB3F; - case 0xE229: - return 0xFEB81; - case 0xE22A: - return 0xFEB31; - case 0xE22B: - return 0xFEB2F; - case 0xE22C: - return 0xFEB40; - case 0xE22D: - return 0xFEB41; - case 0xE22E: - return 0xFEB99; - case 0xE22F: - return 0xFEB9A; - case 0xE230: - return 0xFEB9B; - case 0xE231: - return 0xFEB9C; - case 0xE232: - return 0xFEAF8; - case 0xE233: - return 0xFEAF9; - case 0xE234: - return 0xFEAFA; - case 0xE235: - return 0xFEAFB; - case 0xE236: - return 0xFEAF0; - case 0xE237: - return 0xFEAF2; - case 0xE238: - return 0xFEAF1; - case 0xE239: - return 0xFEAF3; - case 0xE23A: - return 0xFEAFC; - case 0xE23B: - return 0xFEAFD; - case 0xE23C: - return 0xFEAFE; - case 0xE23D: - return 0xFEAFF; - case 0xE23E: - return 0xFE4F8; - case 0xE23F: - return 0xFE02B; - case 0xE240: - return 0xFE02C; - case 0xE241: - return 0xFE02D; - case 0xE242: - return 0xFE02E; - case 0xE243: - return 0xFE02F; - case 0xE244: - return 0xFE030; - case 0xE245: - return 0xFE031; - case 0xE246: - return 0xFE032; - case 0xE247: - return 0xFE033; - case 0xE248: - return 0xFE034; - case 0xE249: - return 0xFE035; - case 0xE24A: - return 0xFE036; - case 0xE24B: - return 0xFE037; - case 0xE24C: - return 0xFEB42; - case 0xE24D: - return 0xFEB27; - case 0xE24E: - return 0xFEB29; - case 0xE24F: - return 0xFEB2D; - case 0xE250: - return 0xFE839; - case 0xE251: - return 0xFE83A; - case 0xE252: - return 0xFEB23; - case 0xE253: - return 0xFE1B4; - case 0xE254: - return 0xFEE77; - case 0xE255: - return 0xFEE78; - case 0xE256: - return 0xFEE79; - case 0xE257: - return 0xFEE7A; - case 0xE258: - return 0xFEE7B; - case 0xE259: - return 0xFEE7C; - case 0xE25A: - return 0xFEE7D; - case 0xE301: - return 0xFE527; - case 0xE302: - return 0xFE4D3; - case 0xE303: - return 0xFE045; - case 0xE304: - return 0xFE03D; - case 0xE305: - return 0xFE046; - case 0xE306: - return 0xFE828; - case 0xE307: - return 0xFE047; - case 0xE308: - return 0xFE048; - case 0xE309: - return 0xFE508; - case 0xE30A: - return 0xFE803; - case 0xE30B: - return 0xFE985; - case 0xE30C: - return 0xFE987; - case 0xE30D: - return 0xFEB43; - case 0xE30E: - return 0xFEB1E; - case 0xE30F: - return 0xFE50A; - case 0xE310: - return 0xFE516; - case 0xE311: - return 0xFEB58; - case 0xE312: - return 0xFE517; - case 0xE313: - return 0xFE53E; - case 0xE314: - return 0xFE50F; - case 0xE315: - return 0xFEB2B; - case 0xE316: - return 0xFE53C; - case 0xE317: - return 0xFE530; - case 0xE318: - return 0xFE4D4; - case 0xE319: - return 0xFE4D5; - case 0xE31A: - return 0xFE4D7; - case 0xE31B: - return 0xFE4D8; - case 0xE31C: - return 0xFE195; - case 0xE31D: - return 0xFE196; - case 0xE31E: - return 0xFE197; - case 0xE31F: - return 0xFE198; - case 0xE320: - return 0xFE199; - case 0xE321: - return 0xFE4D9; - case 0xE322: - return 0xFE4DA; - case 0xE323: - return 0xFE4F0; - case 0xE324: - return 0xFE808; - case 0xE325: - return 0xFE4F2; - case 0xE326: - return 0xFE814; - case 0xE327: - return 0xFEB0D; - case 0xE328: - return 0xFEB11; - case 0xE329: - return 0xFEB12; - case 0xE32A: - return 0xFEB13; - case 0xE32B: - return 0xFEB14; - case 0xE32C: - return 0xFEB15; - case 0xE32D: - return 0xFEB16; - case 0xE32E: - return 0xFEB60; - case 0xE32F: - return 0xFEB68; - case 0xE330: - return 0xFEB5D; - case 0xE331: - return 0xFEB5B; - case 0xE332: - return 0xFEB44; - case 0xE333: - return 0xFEB45; - case 0xE334: - return 0xFEB57; - case 0xE335: - return 0xFEB69; - case 0xE336: - return 0xFEB0A; - case 0xE337: - return 0xFEB0B; - case 0xE338: - return 0xFE984; - case 0xE339: - return 0xFE964; - case 0xE33A: - return 0xFE966; - case 0xE33B: - return 0xFE967; - case 0xE33C: - return 0xFE968; - case 0xE33D: - return 0xFE969; - case 0xE33E: - return 0xFE96A; - case 0xE33F: - return 0xFE96B; - case 0xE340: - return 0xFE963; - case 0xE341: - return 0xFE96C; - case 0xE342: - return 0xFE961; - case 0xE343: - return 0xFE96D; - case 0xE344: - return 0xFE96E; - case 0xE345: - return 0xFE051; - case 0xE346: - return 0xFE052; - case 0xE347: - return 0xFE053; - case 0xE348: - return 0xFE054; - case 0xE349: - return 0xFE055; - case 0xE34A: - return 0xFE056; - case 0xE34B: - return 0xFE511; - case 0xE34C: - return 0xFE96F; - case 0xE34D: - return 0xFE970; - case 0xE401: - return 0xFE345; - case 0xE402: - return 0xFE343; - case 0xE403: - return 0xFE340; - case 0xE404: - return 0xFE333; - case 0xE405: - return 0xFE347; - case 0xE406: - return 0xFE33C; - case 0xE407: - return 0xFE33F; - case 0xE408: - return 0xFE342; - case 0xE409: - return 0xFE32A; - case 0xE40A: - return 0xFE33E; - case 0xE40B: - return 0xFE33B; - case 0xE40C: - return 0xFE32E; - case 0xE40D: - return 0xFE32F; - case 0xE40E: - return 0xFE326; - case 0xE40F: - return 0xFE325; - case 0xE410: - return 0xFE322; - case 0xE411: - return 0xFE33A; - case 0xE412: - return 0xFE334; - case 0xE413: - return 0xFE339; - case 0xE414: - return 0xFE336; - case 0xE415: - return 0xFE338; - case 0xE416: - return 0xFE33D; - case 0xE417: - return 0xFE32D; - case 0xE418: - return 0xFE32C; - case 0xE419: - return 0xFE190; - case 0xE41A: - return 0xFE192; - case 0xE41B: - return 0xFE191; - case 0xE41C: - return 0xFE193; - case 0xE41D: - return 0xFE35B; - case 0xE41E: - return 0xFEB9D; - case 0xE41F: - return 0xFEB9E; - case 0xE420: - return 0xFEB9F; - case 0xE421: - return 0xFEBA0; - case 0xE422: - return 0xFEBA1; - case 0xE423: - return 0xFE351; - case 0xE424: - return 0xFE352; - case 0xE425: - return 0xFE829; - case 0xE426: - return 0xFE353; - case 0xE427: - return 0xFE358; - case 0xE428: - return 0xFE1A0; - case 0xE429: - return 0xFE1A2; - case 0xE42A: - return 0xFE7D6; - case 0xE42B: - return 0xFE7DD; - case 0xE42C: - return 0xFE80E; - case 0xE42D: - return 0xFE7DE; - case 0xE42E: - return 0xFE7E5; - case 0xE42F: - return 0xFE7F1; - case 0xE430: - return 0xFE7F2; - case 0xE431: - return 0xFE7F3; - case 0xE432: - return 0xFE7F4; - case 0xE433: - return 0xFE7FE; - case 0xE434: - return 0xFE7E0; - case 0xE435: - return 0xFE7E2; - case 0xE436: - return 0xFE518; - case 0xE437: - return 0xFEB17; - case 0xE438: - return 0xFE519; - case 0xE439: - return 0xFE51A; - case 0xE43A: - return 0xFE51B; - case 0xE43B: - return 0xFE51C; - case 0xE43C: - return 0xFE007; - case 0xE43D: - return 0xFE82A; - case 0xE43E: - return 0xFE038; - case 0xE43F: - return 0xFE971; - case 0xE440: - return 0xFE51D; - case 0xE441: - return 0xFE1C6; - case 0xE442: - return 0xFE51E; - case 0xE443: - return 0xFE005; - case 0xE444: - return 0xFE049; - case 0xE445: - return 0xFE51F; - case 0xE446: - return 0xFE017; - case 0xE447: - return 0xFE043; - case 0xE448: - return 0xFE513; - case 0xE449: - return 0xFE00A; - case 0xE44A: - return 0xFE00C; - case 0xE44B: - return 0xFE008; - case 0xE44C: - return 0xFE00D; - case 0xE501: - return 0xFE4B8; - case 0xE502: - return 0xFE804; - case 0xE503: - return 0xFE805; - case 0xE504: - return 0xFE4BD; - case 0xE505: - return 0xFE4BE; - case 0xE506: - return 0xFE4BF; - case 0xE507: - return 0xFE802; - case 0xE508: - return 0xFE4C0; - case 0xE509: - return 0xFE4C4; - case 0xE50A: - return 0xFE4C5; - case 0xE50B: - return 0xFE4E5; - case 0xE50C: - return 0xFE4E6; - case 0xE50D: - return 0xFE4E7; - case 0xE50E: - return 0xFE4E8; - case 0xE50F: - return 0xFE4E9; - case 0xE510: - return 0xFE4EA; - case 0xE511: - return 0xFE4EB; - case 0xE512: - return 0xFE4EC; - case 0xE513: - return 0xFE4ED; - case 0xE514: - return 0xFE4EE; - case 0xE515: - return 0xFE1A4; - case 0xE516: - return 0xFE1A5; - case 0xE517: - return 0xFE1A6; - case 0xE518: - return 0xFE1A7; - case 0xE519: - return 0xFE1A8; - case 0xE51A: - return 0xFE1A9; - case 0xE51B: - return 0xFE1AA; - case 0xE51C: - return 0xFE1AB; - case 0xE51D: - return 0xFE4C6; - case 0xE51E: - return 0xFE1B5; - case 0xE51F: - return 0xFE1B6; - case 0xE520: - return 0xFE1C7; - case 0xE521: - return 0xFE1C8; - case 0xE522: - return 0xFE1C9; - case 0xE523: - return 0xFE1BA; - case 0xE524: - return 0xFE1CA; - case 0xE525: - return 0xFE1CB; - case 0xE526: - return 0xFE1CC; - case 0xE527: - return 0xFE1CD; - case 0xE528: - return 0xFE1CE; - case 0xE529: - return 0xFE1CF; - case 0xE52A: - return 0xFE1D0; - case 0xE52B: - return 0xFE1D1; - case 0xE52C: - return 0xFE1D2; - case 0xE52D: - return 0xFE1D3; - case 0xE52E: - return 0xFE1D4; - case 0xE52F: - return 0xFE1D5; - case 0xE530: - return 0xFE1D6; - case 0xE531: - return 0xFE1D7; - case 0xE532: - return 0xFE50B; - case 0xE533: - return 0xFE50C; - case 0xE534: - return 0xFE50D; - case 0xE535: - return 0xFE50E; - case 0xE536: - return 0xFE553; - case 0xE537: - return 0xFEB2A; - case 0xE538: - return 0xFEE70; - case 0xE539: - return 0xFEE71; - case 0xE53A: - return 0xFEE72; - case 0xE53B: - return 0xFEE73; - case 0xE53C: - return 0xFEE74; - case 0xE53D: - return 0xFEE75; - case 0xE53E: - return 0xFEE76; - default: - return codePoint; - } - } - - public static void setCharset(String charset, Part part) throws MessagingException { - part.setHeader(MimeHeader.HEADER_CONTENT_TYPE, - part.getMimeType() + ";\r\n charset=" + getExternalCharset(charset)); - } - - public static String getExternalCharset(String charset) { - if (charset.length() > 17 && charset.startsWith("x-") && - charset.endsWith("-shift_jis-2007")) - return "shift_jis"; - - return charset; - } - - public static ViewableContainer extractPartsFromDraft(Message message) - throws MessagingException { - - Body body = message.getBody(); - if (message.isMimeType("multipart/mixed") && body instanceof MimeMultipart) { - MimeMultipart multipart = (MimeMultipart) body; - - ViewableContainer container; - int count = multipart.getCount(); - if (count >= 1) { - // The first part is either a text/plain or a multipart/alternative - BodyPart firstPart = multipart.getBodyPart(0); - container = extractTextual(firstPart); - - // The rest should be attachments - for (int i = 1; i < count; i++) { - BodyPart bodyPart = multipart.getBodyPart(i); - container.attachments.add(bodyPart); - } - } else { - container = new ViewableContainer("", "", new ArrayList()); - } - - return container; - } - - return extractTextual(message); - } - - private static ViewableContainer extractTextual(Part part) throws MessagingException { - String text = ""; - String html = ""; - List attachments = new ArrayList(); - - Body firstBody = part.getBody(); - if (part.isMimeType("text/plain")) { - String bodyText = getTextFromPart(part); - if (bodyText != null) { - text = bodyText; - html = HtmlConverter.textToHtml(text); - } - } else if (part.isMimeType("multipart/alternative") && - firstBody instanceof MimeMultipart) { - MimeMultipart multipart = (MimeMultipart) firstBody; - for (BodyPart bodyPart : multipart.getBodyParts()) { - String bodyText = getTextFromPart(bodyPart); - if (bodyText != null) { - if (text.isEmpty() && bodyPart.isMimeType("text/plain")) { - text = bodyText; - } else if (html.isEmpty() && bodyPart.isMimeType("text/html")) { - html = bodyText; - } - } - } - } - - return new ViewableContainer(text, html, attachments); - } } diff --git a/src/com/fsck/k9/mail/internet/TextBody.java b/src/com/fsck/k9/mail/internet/TextBody.java index ad6e474ac..d6355662f 100644 --- a/src/com/fsck/k9/mail/internet/TextBody.java +++ b/src/com/fsck/k9/mail/internet/TextBody.java @@ -4,7 +4,11 @@ package com.fsck.k9.mail.internet; import com.fsck.k9.mail.Body; import com.fsck.k9.mail.MessagingException; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import org.apache.james.mime4j.codec.QuotedPrintableOutputStream; import org.apache.james.mime4j.util.MimeUtil; diff --git a/src/com/fsck/k9/mail/internet/Viewable.java b/src/com/fsck/k9/mail/internet/Viewable.java new file mode 100644 index 000000000..6ec32b926 --- /dev/null +++ b/src/com/fsck/k9/mail/internet/Viewable.java @@ -0,0 +1,105 @@ +package com.fsck.k9.mail.internet; + +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.Part; + +import java.util.List; + +/** + * Empty marker class interface the class hierarchy used by + * {@link com.fsck.k9.mailstore.LocalMessageExtractor#extractTextAndAttachments(android.content.Context, com.fsck.k9.mail.Message)}. + * + * @see Viewable.Text + * @see Viewable.Html + * @see Viewable.MessageHeader + * @see Viewable.Alternative + */ +public interface Viewable { + /** + * Class representing textual parts of a message that aren't marked as attachments. + * + * @see com.fsck.k9.mail.internet.MessageExtractor#isPartTextualBody(com.fsck.k9.mail.Part) + */ + static abstract class Textual implements Viewable { + private Part mPart; + + public Textual(Part part) { + mPart = part; + } + + public Part getPart() { + return mPart; + } + } + + /** + * Class representing a {@code text/plain} part of a message. + */ + static class Text extends Textual { + public Text(Part part) { + super(part); + } + } + + /** + * Class representing a {@code text/html} part of a message. + */ + static class Html extends Textual { + public Html(Part part) { + super(part); + } + } + + /** + * Class representing a {@code message/rfc822} part of a message. + * + *

    + * This is used to extract basic header information when the message contents are displayed + * inline. + *

    + */ + static class MessageHeader implements Viewable { + private Part mContainerPart; + private Message mMessage; + + public MessageHeader(Part containerPart, Message message) { + mContainerPart = containerPart; + mMessage = message; + } + + public Part getContainerPart() { + return mContainerPart; + } + + public Message getMessage() { + return mMessage; + } + } + + /** + * Class representing a {@code multipart/alternative} part of a message. + * + *

    + * Only relevant {@code text/plain} and {@code text/html} children are stored in this container + * class. + *

    + */ + static class Alternative implements Viewable { + private List mText; + private List mHtml; + + public Alternative(List text, List html) { + mText = text; + mHtml = html; + } + + public List getText() { + return mText; + } + + public List getHtml() { + return mHtml; + } + } + +} diff --git a/src/com/fsck/k9/mail/transport/SmtpTransport.java b/src/com/fsck/k9/mail/transport/SmtpTransport.java index 568f30b5e..29fae5f1c 100644 --- a/src/com/fsck/k9/mail/transport/SmtpTransport.java +++ b/src/com/fsck/k9/mail/transport/SmtpTransport.java @@ -12,7 +12,7 @@ import com.fsck.k9.mail.filter.EOLConvertingOutputStream; import com.fsck.k9.mail.filter.LineWrapOutputStream; import com.fsck.k9.mail.filter.PeekableInputStream; import com.fsck.k9.mail.filter.SmtpDataStuffing; -import com.fsck.k9.mail.internet.MimeUtility; +import com.fsck.k9.mail.internet.CharsetSupport; import com.fsck.k9.mail.store.StoreConfig; import com.fsck.k9.mail.ssl.TrustedSocketFactory; @@ -463,7 +463,7 @@ public class SmtpTransport extends Transport { new HashMap>(); for (Address address : addresses) { String addressString = address.getAddress(); - String charset = MimeUtility.getCharsetFromAddress(addressString); + String charset = CharsetSupport.getCharsetFromAddress(addressString); List addressesOfCharset = charsetAddressesMap.get(charset); if (addressesOfCharset == null) { addressesOfCharset = new ArrayList(); diff --git a/src/com/fsck/k9/mailstore/LocalFolder.java b/src/com/fsck/k9/mailstore/LocalFolder.java index edf81d931..63e958ec9 100644 --- a/src/com/fsck/k9/mailstore/LocalFolder.java +++ b/src/com/fsck/k9/mailstore/LocalFolder.java @@ -33,7 +33,7 @@ import com.fsck.k9.K9; import com.fsck.k9.Account.MessageFormat; import com.fsck.k9.activity.Search; import com.fsck.k9.mail.MessageRetrievalListener; -import com.fsck.k9.mail.internet.HtmlConverter; +import com.fsck.k9.helper.HtmlConverter; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Body; @@ -51,11 +51,11 @@ import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; -import com.fsck.k9.mail.internet.MimeUtility.ViewableContainer; import com.fsck.k9.mailstore.LockableDatabase.DbCallback; import com.fsck.k9.mailstore.LockableDatabase.WrappedException; import com.fsck.k9.provider.AttachmentProvider; + public class LocalFolder extends Folder implements Serializable { private static final long serialVersionUID = -1973296520918624767L; @@ -1299,14 +1299,14 @@ public class LocalFolder extends Folder implements Serializable { // draft messages because this will cause the values stored in // the identity header to be wrong. ViewableContainer container = - MimeUtility.extractPartsFromDraft(message); + LocalMessageExtractor.extractPartsFromDraft(message); text = container.text; html = container.html; attachments = container.attachments; } else { ViewableContainer container = - MimeUtility.extractTextAndAttachments(LocalFolder.this.localStore.mApplication, message); + LocalMessageExtractor.extractTextAndAttachments(LocalFolder.this.localStore.mApplication, message); attachments = container.attachments; text = container.text; @@ -1412,7 +1412,7 @@ public class LocalFolder extends Folder implements Serializable { message.buildMimeRepresentation(); ViewableContainer container = - MimeUtility.extractTextAndAttachments(LocalFolder.this.localStore.mApplication, message); + LocalMessageExtractor.extractTextAndAttachments(LocalFolder.this.localStore.mApplication, message); List attachments = container.attachments; String text = container.text; diff --git a/src/com/fsck/k9/mailstore/LocalMessage.java b/src/com/fsck/k9/mailstore/LocalMessage.java index 464a6b1e9..05dfacec1 100644 --- a/src/com/fsck/k9/mailstore/LocalMessage.java +++ b/src/com/fsck/k9/mailstore/LocalMessage.java @@ -120,16 +120,16 @@ public class LocalMessage extends MimeMessage { */ public String getTextForDisplay() throws MessagingException { String text = null; // First try and fetch an HTML part. - Part part = MimeUtility.findFirstPartByMimeType(this, "text/html"); + Part part = findFirstPartByMimeType("text/html"); if (part == null) { // If that fails, try and get a text part. - part = MimeUtility.findFirstPartByMimeType(this, "text/plain"); + part = findFirstPartByMimeType("text/plain"); if (part != null && part.getBody() instanceof LocalTextBody) { text = ((LocalTextBody) part.getBody()).getBodyForDisplay(); } } else { // We successfully found an HTML part; do the necessary character set decoding. - text = MimeUtility.getTextFromPart(part); + text = part.getText(); } return text; } diff --git a/src/com/fsck/k9/mailstore/LocalMessageExtractor.java b/src/com/fsck/k9/mailstore/LocalMessageExtractor.java new file mode 100644 index 000000000..546c1f62d --- /dev/null +++ b/src/com/fsck/k9/mailstore/LocalMessageExtractor.java @@ -0,0 +1,464 @@ +package com.fsck.k9.mailstore; + +import android.content.Context; + +import com.fsck.k9.R; +import com.fsck.k9.mail.Address; +import com.fsck.k9.mail.Body; +import com.fsck.k9.mail.BodyPart; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.MessagingException; +import com.fsck.k9.mail.Part; +import com.fsck.k9.helper.HtmlConverter; +import com.fsck.k9.mail.internet.MessageExtractor; +import com.fsck.k9.mail.internet.MimeMultipart; +import com.fsck.k9.mail.internet.Viewable; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import static com.fsck.k9.mail.internet.MimeUtility.getHeaderParameter; + +class LocalMessageExtractor { + private static final String TEXT_DIVIDER = + "------------------------------------------------------------------------"; + private static final int TEXT_DIVIDER_LENGTH = TEXT_DIVIDER.length(); + private static final String FILENAME_PREFIX = "----- "; + private static final int FILENAME_PREFIX_LENGTH = FILENAME_PREFIX.length(); + private static final String FILENAME_SUFFIX = " "; + private static final int FILENAME_SUFFIX_LENGTH = FILENAME_SUFFIX.length(); + + /** + * Extract the viewable textual parts of a message and return the rest as attachments. + * + * @param context A {@link android.content.Context} instance that will be used to get localized strings. + * @return A {@link ViewableContainer} instance containing the textual parts of the message as + * plain text and HTML, and a list of message parts considered attachments. + * + * @throws com.fsck.k9.mail.MessagingException + * In case of an error. + */ + public static ViewableContainer extractTextAndAttachments(Context context, Message message) throws MessagingException { + try { + List attachments = new ArrayList(); + + // Collect all viewable parts + List viewables = MessageExtractor.getViewables(message, attachments); + + /* + * Convert the tree of viewable parts into text and HTML + */ + + // Used to suppress the divider for the first viewable part + boolean hideDivider = true; + + StringBuilder text = new StringBuilder(); + StringBuilder html = new StringBuilder(); + + for (Viewable viewable : viewables) { + if (viewable instanceof Viewable.Textual) { + // This is either a text/plain or text/html part. Fill the variables 'text' and + // 'html', converting between plain text and HTML as necessary. + text.append(buildText(viewable, !hideDivider)); + html.append(buildHtml(viewable, !hideDivider)); + hideDivider = false; + } else if (viewable instanceof Viewable.MessageHeader) { + Viewable.MessageHeader header = (Viewable.MessageHeader) viewable; + Part containerPart = header.getContainerPart(); + Message innerMessage = header.getMessage(); + + addTextDivider(text, containerPart, !hideDivider); + addMessageHeaderText(context, text, innerMessage); + + addHtmlDivider(html, containerPart, !hideDivider); + addMessageHeaderHtml(context, html, innerMessage); + + hideDivider = true; + } else if (viewable instanceof Viewable.Alternative) { + // Handle multipart/alternative contents + Viewable.Alternative alternative = (Viewable.Alternative) viewable; + + /* + * We made sure at least one of text/plain or text/html is present when + * creating the Alternative object. If one part is not present we convert the + * other one to make sure 'text' and 'html' always contain the same text. + */ + List textAlternative = alternative.getText().isEmpty() ? + alternative.getHtml() : alternative.getText(); + List htmlAlternative = alternative.getHtml().isEmpty() ? + alternative.getText() : alternative.getHtml(); + + // Fill the 'text' variable + boolean divider = !hideDivider; + for (Viewable textViewable : textAlternative) { + text.append(buildText(textViewable, divider)); + divider = true; + } + + // Fill the 'html' variable + divider = !hideDivider; + for (Viewable htmlViewable : htmlAlternative) { + html.append(buildHtml(htmlViewable, divider)); + divider = true; + } + hideDivider = false; + } + } + + return new ViewableContainer(text.toString(), html.toString(), attachments); + } catch (Exception e) { + throw new MessagingException("Couldn't extract viewable parts", e); + } + } + + public static ViewableContainer extractPartsFromDraft(Message message) + throws MessagingException { + + Body body = message.getBody(); + if (message.isMimeType("multipart/mixed") && body instanceof MimeMultipart) { + MimeMultipart multipart = (MimeMultipart) body; + + ViewableContainer container; + int count = multipart.getCount(); + if (count >= 1) { + // The first part is either a text/plain or a multipart/alternative + BodyPart firstPart = multipart.getBodyPart(0); + container = extractTextual(firstPart); + + // The rest should be attachments + for (int i = 1; i < count; i++) { + BodyPart bodyPart = multipart.getBodyPart(i); + container.attachments.add(bodyPart); + } + } else { + container = new ViewableContainer("", "", new ArrayList()); + } + + return container; + } + + return extractTextual(message); + } + + /** + * Use the contents of a {@link com.fsck.k9.mail.internet.Viewable} to create the HTML to be displayed. + * + *

    + * This will use {@link com.fsck.k9.helper.HtmlConverter#textToHtml(String)} to convert plain text parts + * to HTML if necessary. + *

    + * + * @param viewable + * The viewable part to build the HTML from. + * @param prependDivider + * {@code true}, if the HTML divider should be inserted as first element. + * {@code false}, otherwise. + * + * @return The contents of the supplied viewable instance as HTML. + */ + private static StringBuilder buildHtml(Viewable viewable, boolean prependDivider) + { + StringBuilder html = new StringBuilder(); + if (viewable instanceof Viewable.Textual) { + Part part = ((Viewable.Textual)viewable).getPart(); + addHtmlDivider(html, part, prependDivider); + + String t = part.getText(); + if (t == null) { + t = ""; + } else if (viewable instanceof Viewable.Text) { + t = HtmlConverter.textToHtml(t); + } + html.append(t); + } else if (viewable instanceof Viewable.Alternative) { + // That's odd - an Alternative as child of an Alternative; go ahead and try to use the + // text/html child; fall-back to the text/plain part. + Viewable.Alternative alternative = (Viewable.Alternative) viewable; + + List htmlAlternative = alternative.getHtml().isEmpty() ? + alternative.getText() : alternative.getHtml(); + + boolean divider = prependDivider; + for (Viewable htmlViewable : htmlAlternative) { + html.append(buildHtml(htmlViewable, divider)); + divider = true; + } + } + + return html; + } + + private static StringBuilder buildText(Viewable viewable, boolean prependDivider) + { + StringBuilder text = new StringBuilder(); + if (viewable instanceof Viewable.Textual) { + Part part = ((Viewable.Textual)viewable).getPart(); + addTextDivider(text, part, prependDivider); + + String t = part.getText(); + if (t == null) { + t = ""; + } else if (viewable instanceof Viewable.Html) { + t = HtmlConverter.htmlToText(t); + } + text.append(t); + } else if (viewable instanceof Viewable.Alternative) { + // That's odd - an Alternative as child of an Alternative; go ahead and try to use the + // text/plain child; fall-back to the text/html part. + Viewable.Alternative alternative = (Viewable.Alternative) viewable; + + List textAlternative = alternative.getText().isEmpty() ? + alternative.getHtml() : alternative.getText(); + + boolean divider = prependDivider; + for (Viewable textViewable : textAlternative) { + text.append(buildText(textViewable, divider)); + divider = true; + } + } + + return text; + } + + /** + * Add an HTML divider between two HTML message parts. + * + * @param html + * The {@link StringBuilder} to append the divider to. + * @param part + * The message part that will follow after the divider. This is used to extract the + * part's name. + * @param prependDivider + * {@code true}, if the divider should be appended. {@code false}, otherwise. + */ + private static void addHtmlDivider(StringBuilder html, Part part, boolean prependDivider) { + if (prependDivider) { + String filename = getPartName(part); + + html.append("

    "); + html.append(filename); + html.append("

    "); + } + } + + /** + * Get the name of the message part. + * + * @param part + * The part to get the name for. + * + * @return The (file)name of the part if available. An empty string, otherwise. + */ + private static String getPartName(Part part) { + try { + String disposition = part.getDisposition(); + if (disposition != null) { + String name = getHeaderParameter(disposition, "filename"); + return (name == null) ? "" : name; + } + } + catch (MessagingException e) { /* ignore */ } + + return ""; + } + + /** + * Add a plain text divider between two plain text message parts. + * + * @param text + * The {@link StringBuilder} to append the divider to. + * @param part + * The message part that will follow after the divider. This is used to extract the + * part's name. + * @param prependDivider + * {@code true}, if the divider should be appended. {@code false}, otherwise. + */ + private static void addTextDivider(StringBuilder text, Part part, boolean prependDivider) { + if (prependDivider) { + String filename = getPartName(part); + + text.append("\r\n\r\n"); + int len = filename.length(); + if (len > 0) { + if (len > TEXT_DIVIDER_LENGTH - FILENAME_PREFIX_LENGTH - FILENAME_SUFFIX_LENGTH) { + filename = filename.substring(0, TEXT_DIVIDER_LENGTH - FILENAME_PREFIX_LENGTH - + FILENAME_SUFFIX_LENGTH - 3) + "..."; + } + text.append(FILENAME_PREFIX); + text.append(filename); + text.append(FILENAME_SUFFIX); + text.append(TEXT_DIVIDER.substring(0, TEXT_DIVIDER_LENGTH - + FILENAME_PREFIX_LENGTH - filename.length() - FILENAME_SUFFIX_LENGTH)); + } else { + text.append(TEXT_DIVIDER); + } + text.append("\r\n\r\n"); + } + } + + /** + * Extract important header values from a message to display inline (plain text version). + * + * @param context + * A {@link android.content.Context} instance that will be used to get localized strings. + * @param text + * The {@link StringBuilder} that will receive the (plain text) output. + * @param message + * The message to extract the header values from. + * + * @throws com.fsck.k9.mail.MessagingException + * In case of an error. + */ + private static void addMessageHeaderText(Context context, StringBuilder text, Message message) + throws MessagingException { + // From: + Address[] from = message.getFrom(); + if (from != null && from.length > 0) { + text.append(context.getString(R.string.message_compose_quote_header_from)); + text.append(' '); + text.append(Address.toString(from)); + text.append("\r\n"); + } + + // To: + Address[] to = message.getRecipients(Message.RecipientType.TO); + if (to != null && to.length > 0) { + text.append(context.getString(R.string.message_compose_quote_header_to)); + text.append(' '); + text.append(Address.toString(to)); + text.append("\r\n"); + } + + // Cc: + Address[] cc = message.getRecipients(Message.RecipientType.CC); + if (cc != null && cc.length > 0) { + text.append(context.getString(R.string.message_compose_quote_header_cc)); + text.append(' '); + text.append(Address.toString(cc)); + text.append("\r\n"); + } + + // Date: + Date date = message.getSentDate(); + if (date != null) { + text.append(context.getString(R.string.message_compose_quote_header_send_date)); + text.append(' '); + text.append(date.toString()); + text.append("\r\n"); + } + + // Subject: + String subject = message.getSubject(); + text.append(context.getString(R.string.message_compose_quote_header_subject)); + text.append(' '); + if (subject == null) { + text.append(context.getString(R.string.general_no_subject)); + } else { + text.append(subject); + } + text.append("\r\n\r\n"); + } + + /** + * Extract important header values from a message to display inline (HTML version). + * + * @param context + * A {@link android.content.Context} instance that will be used to get localized strings. + * @param html + * The {@link StringBuilder} that will receive the (HTML) output. + * @param message + * The message to extract the header values from. + * + * @throws com.fsck.k9.mail.MessagingException + * In case of an error. + */ + private static void addMessageHeaderHtml(Context context, StringBuilder html, Message message) + throws MessagingException { + + html.append(""); + + // From: + Address[] from = message.getFrom(); + if (from != null && from.length > 0) { + addTableRow(html, context.getString(R.string.message_compose_quote_header_from), + Address.toString(from)); + } + + // To: + Address[] to = message.getRecipients(Message.RecipientType.TO); + if (to != null && to.length > 0) { + addTableRow(html, context.getString(R.string.message_compose_quote_header_to), + Address.toString(to)); + } + + // Cc: + Address[] cc = message.getRecipients(Message.RecipientType.CC); + if (cc != null && cc.length > 0) { + addTableRow(html, context.getString(R.string.message_compose_quote_header_cc), + Address.toString(cc)); + } + + // Date: + Date date = message.getSentDate(); + if (date != null) { + addTableRow(html, context.getString(R.string.message_compose_quote_header_send_date), + date.toString()); + } + + // Subject: + String subject = message.getSubject(); + addTableRow(html, context.getString(R.string.message_compose_quote_header_subject), + (subject == null) ? context.getString(R.string.general_no_subject) : subject); + + html.append("
    "); + } + + /** + * Output an HTML table two column row with some hardcoded style. + * + * @param html + * The {@link StringBuilder} that will receive the output. + * @param header + * The string to be put in the {@code TH} element. + * @param value + * The string to be put in the {@code TD} element. + */ + private static void addTableRow(StringBuilder html, String header, String value) { + html.append(""); + html.append(header); + html.append(""); + html.append(""); + html.append(value); + html.append(""); + } + + private static ViewableContainer extractTextual(Part part) throws MessagingException { + String text = ""; + String html = ""; + List attachments = new ArrayList(); + + Body firstBody = part.getBody(); + if (part.isMimeType("text/plain")) { + String bodyText = part.getText(); + if (bodyText != null) { + text = bodyText; + html = HtmlConverter.textToHtml(text); + } + } else if (part.isMimeType("multipart/alternative") && + firstBody instanceof MimeMultipart) { + MimeMultipart multipart = (MimeMultipart) firstBody; + for (BodyPart bodyPart : multipart.getBodyParts()) { + String bodyText = bodyPart.getText(); + if (bodyText != null) { + if (text.isEmpty() && bodyPart.isMimeType("text/plain")) { + text = bodyText; + } else if (html.isEmpty() && bodyPart.isMimeType("text/html")) { + html = bodyText; + } + } + } + } + return new ViewableContainer(text, html, attachments); + } +} diff --git a/src/com/fsck/k9/mailstore/ViewableContainer.java b/src/com/fsck/k9/mailstore/ViewableContainer.java new file mode 100644 index 000000000..2e538cd22 --- /dev/null +++ b/src/com/fsck/k9/mailstore/ViewableContainer.java @@ -0,0 +1,34 @@ +package com.fsck.k9.mailstore; + +import com.fsck.k9.mail.Part; + +import java.util.List; + +/** + * Store viewable text of a message as plain text and HTML, and the parts considered + * attachments. + * + * @see LocalMessageExtractor#extractTextAndAttachments(android.content.Context, com.fsck.k9.mail.Message) + */ +class ViewableContainer { + /** + * The viewable text of the message in plain text. + */ + public final String text; + + /** + * The viewable text of the message in HTML. + */ + public final String html; + + /** + * The parts of the message considered attachments (everything not viewable). + */ + public final List attachments; + + public ViewableContainer(String text, String html, List attachments) { + this.text = text; + this.html = html; + this.attachments = attachments; + } +} diff --git a/src/com/fsck/k9/view/MessageOpenPgpView.java b/src/com/fsck/k9/view/MessageOpenPgpView.java index 1b224112f..65bff60c8 100644 --- a/src/com/fsck/k9/view/MessageOpenPgpView.java +++ b/src/com/fsck/k9/view/MessageOpenPgpView.java @@ -215,8 +215,7 @@ public class MessageOpenPgpView extends LinearLayout { } else { try { // check for PGP/MIME encryption - Part pgp = MimeUtility - .findFirstPartByMimeType(message, "application/pgp-encrypted"); + Part pgp = message.findFirstPartByMimeType("application/pgp-encrypted"); if (pgp != null) { Toast.makeText(mContext, R.string.pgp_mime_unsupported, Toast.LENGTH_LONG) .show(); @@ -241,12 +240,12 @@ public class MessageOpenPgpView extends LinearLayout { public void run() { try { // get data String - Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); + Part part = message.findFirstPartByMimeType("text/plain"); if (part == null) { - part = MimeUtility.findFirstPartByMimeType(message, "text/html"); + part = message.findFirstPartByMimeType("text/html"); } if (part != null) { - mData = MimeUtility.getTextFromPart(part); + mData = part.getText(); } // wait for service to be bound diff --git a/src/com/fsck/k9/view/MessageWebView.java b/src/com/fsck/k9/view/MessageWebView.java index 07faaf3e1..3198d4723 100644 --- a/src/com/fsck/k9/view/MessageWebView.java +++ b/src/com/fsck/k9/view/MessageWebView.java @@ -10,7 +10,7 @@ import android.widget.Toast; import com.fsck.k9.K9; import com.fsck.k9.R; -import com.fsck.k9.mail.internet.HtmlConverter; +import com.fsck.k9.helper.HtmlConverter; public class MessageWebView extends RigidWebView { diff --git a/src/com/fsck/k9/view/SingleMessageView.java b/src/com/fsck/k9/view/SingleMessageView.java index 3ebdd89a3..25852ee17 100644 --- a/src/com/fsck/k9/view/SingleMessageView.java +++ b/src/com/fsck/k9/view/SingleMessageView.java @@ -46,7 +46,7 @@ import com.fsck.k9.fragment.MessageViewFragment; import com.fsck.k9.helper.ClipboardManager; import com.fsck.k9.helper.Contacts; import com.fsck.k9.helper.FileHelper; -import com.fsck.k9.mail.internet.HtmlConverter; +import com.fsck.k9.helper.HtmlConverter; import com.fsck.k9.helper.UrlEncodingHelper; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Address; diff --git a/tests-on-jvm/src/com/fsck/k9/mail/internet/TextBodyBuilderTest.java b/tests-on-jvm/src/com/fsck/k9/activity/TextBodyBuilderTest.java similarity index 99% rename from tests-on-jvm/src/com/fsck/k9/mail/internet/TextBodyBuilderTest.java rename to tests-on-jvm/src/com/fsck/k9/activity/TextBodyBuilderTest.java index 7c3b096dd..1a924ea0c 100644 --- a/tests-on-jvm/src/com/fsck/k9/mail/internet/TextBodyBuilderTest.java +++ b/tests-on-jvm/src/com/fsck/k9/activity/TextBodyBuilderTest.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.internet; +package com.fsck.k9.activity; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @@ -8,6 +8,7 @@ import org.junit.runner.RunWith; import com.fsck.k9.Account.QuoteStyle; import com.fsck.k9.activity.TextBodyBuilder; +import com.fsck.k9.mail.internet.TextBody; class TestingTextBodyBuilder extends TextBodyBuilder { diff --git a/tests-on-jvm/src/com/fsck/k9/mail/internet/CharsetSupportTest.java b/tests-on-jvm/src/com/fsck/k9/mail/internet/CharsetSupportTest.java new file mode 100644 index 000000000..1b314642c --- /dev/null +++ b/tests-on-jvm/src/com/fsck/k9/mail/internet/CharsetSupportTest.java @@ -0,0 +1,94 @@ +package com.fsck.k9.mail.internet; + +import junit.framework.TestCase; + +public class CharsetSupportTest extends TestCase { + + public void testFixupCharset() throws Exception { + String charsetOnMail; + String expect; + + charsetOnMail = "CP932"; + expect = "shift_jis"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, new MimeMessage())); + +// charsetOnMail = "koi8-u"; +// expect = "koi8-r"; +// assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, new MimeMessage())); + + MimeMessage message; + + message= new MimeMessage(); + message.setHeader("From", "aaa@docomo.ne.jp"); + charsetOnMail = "shift_jis"; + expect = "x-docomo-shift_jis-2007"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message)); + + message = new MimeMessage(); + message.setHeader("From", "aaa@dwmail.jp"); + charsetOnMail = "shift_jis"; + expect = "x-docomo-shift_jis-2007"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message)); + + message = new MimeMessage(); + message.setHeader("From", "aaa@pdx.ne.jp"); + charsetOnMail = "shift_jis"; + expect = "x-docomo-shift_jis-2007"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message)); + + message = new MimeMessage(); + message.setHeader("From", "aaa@willcom.com"); + charsetOnMail = "shift_jis"; + expect = "x-docomo-shift_jis-2007"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message)); + + message = new MimeMessage(); + message.setHeader("From", "aaa@emnet.ne.jp"); + charsetOnMail = "shift_jis"; + expect = "x-docomo-shift_jis-2007"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message)); + + message = new MimeMessage(); + message.setHeader("From", "aaa@emobile.ne.jp"); + charsetOnMail = "shift_jis"; + expect = "x-docomo-shift_jis-2007"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message)); + + message = new MimeMessage(); + message.setHeader("From", "aaa@softbank.ne.jp"); + charsetOnMail = "shift_jis"; + expect = "x-softbank-shift_jis-2007"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message)); + + message = new MimeMessage(); + message.setHeader("From", "aaa@vodafone.ne.jp"); + charsetOnMail = "shift_jis"; + expect = "x-softbank-shift_jis-2007"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message)); + + message = new MimeMessage(); + message.setHeader("From", "aaa@disney.ne.jp"); + charsetOnMail = "shift_jis"; + expect = "x-softbank-shift_jis-2007"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message)); + + message = new MimeMessage(); + message.setHeader("From", "aaa@vertuclub.ne.jp"); + charsetOnMail = "shift_jis"; + expect = "x-softbank-shift_jis-2007"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message)); + + message = new MimeMessage(); + message.setHeader("From", "aaa@ezweb.ne.jp"); + charsetOnMail = "shift_jis"; + expect = "x-kddi-shift_jis-2007"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message)); + + message = new MimeMessage(); + message.setHeader("From", "aaa@ido.ne.jp"); + charsetOnMail = "shift_jis"; + expect = "x-kddi-shift_jis-2007"; + assertEquals(expect, CharsetSupport.fixupCharset(charsetOnMail, message)); + + } +} \ No newline at end of file diff --git a/tests-on-jvm/src/com/fsck/k9/mail/internet/MimeUtilityTest.java b/tests-on-jvm/src/com/fsck/k9/mail/internet/MimeUtilityTest.java index e723cb562..fe07dddcb 100644 --- a/tests-on-jvm/src/com/fsck/k9/mail/internet/MimeUtilityTest.java +++ b/tests-on-jvm/src/com/fsck/k9/mail/internet/MimeUtilityTest.java @@ -8,15 +8,6 @@ import com.fsck.k9.mail.MessagingException; import junit.framework.TestCase; public class MimeUtilityTest extends TestCase { - - protected void setUp() throws Exception { - super.setUp(); - } - - protected void tearDown() throws Exception { - super.tearDown(); - } - public void testGetHeaderParameter() { String result; @@ -55,93 +46,4 @@ public class MimeUtilityTest extends TestCase { result = MimeUtility.getHeaderParameter("text/HTML ; charset=\"windows-1251\"", null); assertEquals("text/HTML", result); } - - public void testFixupCharset() throws MessagingException { - String charsetOnMail; - String expect; - - charsetOnMail = "CP932"; - expect = "shift_jis"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, new MimeMessage())); - -// charsetOnMail = "koi8-u"; -// expect = "koi8-r"; -// assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, new MimeMessage())); - - MimeMessage message; - - message= new MimeMessage(); - message.setHeader("From", "aaa@docomo.ne.jp"); - charsetOnMail = "shift_jis"; - expect = "x-docomo-shift_jis-2007"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message)); - - message = new MimeMessage(); - message.setHeader("From", "aaa@dwmail.jp"); - charsetOnMail = "shift_jis"; - expect = "x-docomo-shift_jis-2007"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message)); - - message = new MimeMessage(); - message.setHeader("From", "aaa@pdx.ne.jp"); - charsetOnMail = "shift_jis"; - expect = "x-docomo-shift_jis-2007"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message)); - - message = new MimeMessage(); - message.setHeader("From", "aaa@willcom.com"); - charsetOnMail = "shift_jis"; - expect = "x-docomo-shift_jis-2007"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message)); - - message = new MimeMessage(); - message.setHeader("From", "aaa@emnet.ne.jp"); - charsetOnMail = "shift_jis"; - expect = "x-docomo-shift_jis-2007"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message)); - - message = new MimeMessage(); - message.setHeader("From", "aaa@emobile.ne.jp"); - charsetOnMail = "shift_jis"; - expect = "x-docomo-shift_jis-2007"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message)); - - message = new MimeMessage(); - message.setHeader("From", "aaa@softbank.ne.jp"); - charsetOnMail = "shift_jis"; - expect = "x-softbank-shift_jis-2007"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message)); - - message = new MimeMessage(); - message.setHeader("From", "aaa@vodafone.ne.jp"); - charsetOnMail = "shift_jis"; - expect = "x-softbank-shift_jis-2007"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message)); - - message = new MimeMessage(); - message.setHeader("From", "aaa@disney.ne.jp"); - charsetOnMail = "shift_jis"; - expect = "x-softbank-shift_jis-2007"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message)); - - message = new MimeMessage(); - message.setHeader("From", "aaa@vertuclub.ne.jp"); - charsetOnMail = "shift_jis"; - expect = "x-softbank-shift_jis-2007"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message)); - - message = new MimeMessage(); - message.setHeader("From", "aaa@ezweb.ne.jp"); - charsetOnMail = "shift_jis"; - expect = "x-kddi-shift_jis-2007"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message)); - - message = new MimeMessage(); - message.setHeader("From", "aaa@ido.ne.jp"); - charsetOnMail = "shift_jis"; - expect = "x-kddi-shift_jis-2007"; - assertEquals(expect, MimeUtility.fixupCharset(charsetOnMail, message)); - - } - } diff --git a/tests/src/com/fsck/k9/mail/internet/HtmlConverterTest.java b/tests/src/com/fsck/k9/helper/HtmlConverterTest.java similarity index 99% rename from tests/src/com/fsck/k9/mail/internet/HtmlConverterTest.java rename to tests/src/com/fsck/k9/helper/HtmlConverterTest.java index 7faea3b18..32aa241ed 100644 --- a/tests/src/com/fsck/k9/mail/internet/HtmlConverterTest.java +++ b/tests/src/com/fsck/k9/helper/HtmlConverterTest.java @@ -1,4 +1,6 @@ -package com.fsck.k9.mail.internet; +package com.fsck.k9.helper; + +import com.fsck.k9.helper.HtmlConverter; import junit.framework.TestCase; diff --git a/tests/src/com/fsck/k9/mail/MessageTest.java b/tests/src/com/fsck/k9/mail/MessageTest.java index 3e56b228d..1fabfc534 100644 --- a/tests/src/com/fsck/k9/mail/MessageTest.java +++ b/tests/src/com/fsck/k9/mail/MessageTest.java @@ -14,11 +14,11 @@ import android.test.AndroidTestCase; import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.internet.BinaryTempFileBody; import com.fsck.k9.mail.internet.BinaryTempFileMessageBody; +import com.fsck.k9.mail.internet.CharsetSupport; import com.fsck.k9.mail.internet.MimeBodyPart; import com.fsck.k9.mail.internet.MimeHeader; import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMultipart; -import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; public class MessageTest extends AndroidTestCase { @@ -356,7 +356,7 @@ public class MessageTest extends AndroidTestCase { + "End of test.\r\n"); textBody.setCharset("utf-8"); MimeBodyPart bodyPart = new MimeBodyPart(textBody, "text/plain"); - MimeUtility.setCharset("utf-8", bodyPart); + CharsetSupport.setCharset("utf-8", bodyPart); bodyPart.setEncoding(encoding); return bodyPart; } diff --git a/tests/src/com/fsck/k9/mail/internet/ViewablesTest.java b/tests/src/com/fsck/k9/mailstore/LocalMessageExtractorTest.java similarity index 89% rename from tests/src/com/fsck/k9/mail/internet/ViewablesTest.java rename to tests/src/com/fsck/k9/mailstore/LocalMessageExtractorTest.java index cceb4bff5..2fc007caf 100644 --- a/tests/src/com/fsck/k9/mail/internet/ViewablesTest.java +++ b/tests/src/com/fsck/k9/mailstore/LocalMessageExtractorTest.java @@ -1,4 +1,4 @@ -package com.fsck.k9.mail.internet; +package com.fsck.k9.mailstore; import java.util.Date; import java.util.Locale; @@ -9,9 +9,14 @@ import com.fsck.k9.activity.K9ActivityCommon; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Message.RecipientType; -import com.fsck.k9.mail.internet.MimeUtility.ViewableContainer; +import com.fsck.k9.mail.internet.MimeBodyPart; +import com.fsck.k9.mail.internet.MimeMessage; +import com.fsck.k9.mail.internet.MimeMultipart; +import com.fsck.k9.mail.internet.TextBody; -public class ViewablesTest extends AndroidTestCase { +import static com.fsck.k9.mailstore.LocalMessageExtractor.extractTextAndAttachments; + +public class LocalMessageExtractorTest extends AndroidTestCase { public void testSimplePlainTextMessage() throws MessagingException { String bodyText = "K-9 Mail rocks :>"; @@ -24,7 +29,7 @@ public class ViewablesTest extends AndroidTestCase { message.setBody(body); // Extract text - ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message); + ViewableContainer container = extractTextAndAttachments(getContext(), message); String expectedText = bodyText; String expectedHtml = @@ -48,7 +53,7 @@ public class ViewablesTest extends AndroidTestCase { message.setBody(body); // Extract text - ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message); + ViewableContainer container = extractTextAndAttachments(getContext(), message); String expectedText = "K-9 Mail rocks :>"; String expectedHtml = @@ -78,7 +83,7 @@ public class ViewablesTest extends AndroidTestCase { message.setBody(multipart); // Extract text - ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message); + ViewableContainer container = extractTextAndAttachments(getContext(), message); String expectedText = bodyText1 + "\r\n\r\n" + @@ -134,7 +139,7 @@ public class ViewablesTest extends AndroidTestCase { message.setBody(multipart); // Extract text - ViewableContainer container = MimeUtility.extractTextAndAttachments(getContext(), message); + ViewableContainer container = extractTextAndAttachments(getContext(), message); String expectedText = bodyText + From 36ef6df01837c277ece20f9a064c29f451a6e094 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Mon, 15 Dec 2014 12:42:05 +0100 Subject: [PATCH 18/29] Remove unused method --- src/com/fsck/k9/mail/BodyPart.java | 11 ----------- src/com/fsck/k9/mail/Message.java | 15 --------------- src/com/fsck/k9/mail/Part.java | 2 -- .../fsck/k9/mail/internet/MessageExtractor.java | 16 +++++++++++++++- .../fsck/k9/mailstore/LocalMessageExtractor.java | 1 + 5 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/com/fsck/k9/mail/BodyPart.java b/src/com/fsck/k9/mail/BodyPart.java index 84e4324f5..884b03ae1 100644 --- a/src/com/fsck/k9/mail/BodyPart.java +++ b/src/com/fsck/k9/mail/BodyPart.java @@ -17,17 +17,6 @@ public abstract class BodyPart implements Part { public abstract void setEncoding(String encoding) throws MessagingException; - @Override - public String getContentDisposition() { - try { - String disposition = getDisposition(); - if (disposition != null) { - return MimeUtility.getHeaderParameter(disposition, null); - } - } catch (MessagingException e) { /* ignore */ } - return null; - } - @Override public String getText() { return MessageExtractor.getTextFromPart(this); diff --git a/src/com/fsck/k9/mail/Message.java b/src/com/fsck/k9/mail/Message.java index f48eaf559..c1b4a09a5 100644 --- a/src/com/fsck/k9/mail/Message.java +++ b/src/com/fsck/k9/mail/Message.java @@ -270,21 +270,6 @@ public abstract class Message implements Part, CompositeBody { @Override public abstract Message clone(); - /** - * Get the value of the {@code Content-Disposition} header. - * @return The value of the {@code Content-Disposition} header if available. {@code null}, otherwise. - */ - public String getContentDisposition() { - try { - String disposition = getDisposition(); - if (disposition != null) { - return MimeUtility.getHeaderParameter(disposition, null); - } - } - catch (MessagingException e) { /* ignore */ } - return null; - } - @Override public String getText() { return MessageExtractor.getTextFromPart(this); diff --git a/src/com/fsck/k9/mail/Part.java b/src/com/fsck/k9/mail/Part.java index c890cd56e..bcf5dcc5e 100644 --- a/src/com/fsck/k9/mail/Part.java +++ b/src/com/fsck/k9/mail/Part.java @@ -17,8 +17,6 @@ public interface Part { String getDisposition() throws MessagingException; - String getContentDisposition(); - String getContentId() throws MessagingException; String[] getHeader(String name) throws MessagingException; diff --git a/src/com/fsck/k9/mail/internet/MessageExtractor.java b/src/com/fsck/k9/mail/internet/MessageExtractor.java index 5fedd3d28..2f21763ed 100644 --- a/src/com/fsck/k9/mail/internet/MessageExtractor.java +++ b/src/com/fsck/k9/mail/internet/MessageExtractor.java @@ -25,6 +25,8 @@ import static com.fsck.k9.mail.internet.Viewable.Alternative; import static com.fsck.k9.mail.internet.Viewable.Textual; public class MessageExtractor { + private MessageExtractor() {} + public static String getTextFromPart(Part part) { try { if ((part != null) && (part.getBody() != null)) { @@ -157,7 +159,7 @@ public class MessageExtractor { } } } else if (body instanceof Message && - !("attachment".equalsIgnoreCase(part.getContentDisposition()))) { + !("attachment".equalsIgnoreCase(getContentDisposition(part)))) { /* * We only care about message/rfc822 parts whose Content-Disposition header has a value * other than "attachment". @@ -407,4 +409,16 @@ public class MessageExtractor { return false; } } + + + + public static String getContentDisposition(Part part) { + try { + String disposition = part.getDisposition(); + if (disposition != null) { + return MimeUtility.getHeaderParameter(disposition, null); + } + } catch (MessagingException e) { /* ignore */ } + return null; + } } diff --git a/src/com/fsck/k9/mailstore/LocalMessageExtractor.java b/src/com/fsck/k9/mailstore/LocalMessageExtractor.java index 546c1f62d..f0e11902b 100644 --- a/src/com/fsck/k9/mailstore/LocalMessageExtractor.java +++ b/src/com/fsck/k9/mailstore/LocalMessageExtractor.java @@ -29,6 +29,7 @@ class LocalMessageExtractor { private static final String FILENAME_SUFFIX = " "; private static final int FILENAME_SUFFIX_LENGTH = FILENAME_SUFFIX.length(); + private LocalMessageExtractor() {} /** * Extract the viewable textual parts of a message and return the rest as attachments. * From 2a2e18e8b68aca453685c6741abd632d6bc500db Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Mon, 15 Dec 2014 12:45:04 +0100 Subject: [PATCH 19/29] WS / visibility --- src/com/fsck/k9/mail/internet/MessageExtractor.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/com/fsck/k9/mail/internet/MessageExtractor.java b/src/com/fsck/k9/mail/internet/MessageExtractor.java index 2f21763ed..b710171af 100644 --- a/src/com/fsck/k9/mail/internet/MessageExtractor.java +++ b/src/com/fsck/k9/mail/internet/MessageExtractor.java @@ -410,9 +410,7 @@ public class MessageExtractor { } } - - - public static String getContentDisposition(Part part) { + private static String getContentDisposition(Part part) { try { String disposition = part.getDisposition(); if (disposition != null) { From b443af43aed63f25a82bda13c50c807f6c619fb8 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Mon, 15 Dec 2014 13:01:13 +0100 Subject: [PATCH 20/29] Cleanup --- .../k9/mail/internet/MessageExtractor.java | 21 ++++++----- .../fsck/k9/mail/internet/MimeUtility.java | 2 +- src/com/fsck/k9/mail/internet/Viewable.java | 13 ++++--- .../k9/mailstore/LocalMessageExtractor.java | 35 +++++++++++-------- 4 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/com/fsck/k9/mail/internet/MessageExtractor.java b/src/com/fsck/k9/mail/internet/MessageExtractor.java index b710171af..a4b997116 100644 --- a/src/com/fsck/k9/mail/internet/MessageExtractor.java +++ b/src/com/fsck/k9/mail/internet/MessageExtractor.java @@ -22,6 +22,9 @@ import java.util.regex.Pattern; import static com.fsck.k9.mail.internet.CharsetSupport.fixupCharset; import static com.fsck.k9.mail.internet.MimeUtility.getHeaderParameter; import static com.fsck.k9.mail.internet.Viewable.Alternative; +import static com.fsck.k9.mail.internet.Viewable.Html; +import static com.fsck.k9.mail.internet.Viewable.MessageHeader; +import static com.fsck.k9.mail.internet.Viewable.Text; import static com.fsck.k9.mail.internet.Viewable.Textual; public class MessageExtractor { @@ -167,7 +170,7 @@ public class MessageExtractor { Message message = (Message) body; // We add the Message object so we can extract the filename later. - viewables.add(new Viewable.MessageHeader(part, message)); + viewables.add(new MessageHeader(part, message)); // Recurse to grab all viewable parts and attachments from that message. viewables.addAll(getViewables(message, attachments)); @@ -177,10 +180,10 @@ public class MessageExtractor { */ String mimeType = part.getMimeType(); if (mimeType.equalsIgnoreCase("text/plain")) { - Viewable.Text text = new Viewable.Text(part); + Text text = new Text(part); viewables.add(text); } else { - Viewable.Html html = new Viewable.Html(part); + Html html = new Html(part); viewables.add(html); } } else { @@ -220,7 +223,7 @@ public class MessageExtractor { * @param directChild If {@code true}, this method will return after the first {@code text/plain} was * found. * - * @return A list of {@link Viewable.Text} viewables. + * @return A list of {@link Text} viewables. * * @throws MessagingException * In case of an error. @@ -254,7 +257,7 @@ public class MessageExtractor { } } } else if (isPartTextualBody(part) && part.getMimeType().equalsIgnoreCase("text/plain")) { - Viewable.Text text = new Viewable.Text(part); + Text text = new Text(part); viewables.add(text); if (directChild) { break; @@ -274,7 +277,7 @@ public class MessageExtractor { * @param directChild If {@code true}, this method will add all {@code text/html} parts except the first * found to 'attachments'. * - * @return A list of {@link Viewable.Text} viewables. + * @return A list of {@link Text} viewables. * * @throws MessagingException In case of an error. */ @@ -314,7 +317,7 @@ public class MessageExtractor { } } else if (!(directChild && partFound) && isPartTextualBody(part) && part.getMimeType().equalsIgnoreCase("text/html")) { - Viewable.Html html = new Viewable.Html(part); + Html html = new Html(part); viewables.add(html); partFound = true; } else if (!knownTextParts.contains(part)) { @@ -359,8 +362,8 @@ public class MessageExtractor { * * @return The set of viewable {@code Part}s. * - * @see MimeUtility#findHtmlPart(Multipart, Set, List, boolean) - * @see MimeUtility#findAttachments(Multipart, Set, List) + * @see MessageExtractor#findHtmlPart(Multipart, Set, List, boolean) + * @see MessageExtractor#findAttachments(Multipart, Set, List) */ private static Set getParts(List viewables) { Set parts = new HashSet(); diff --git a/src/com/fsck/k9/mail/internet/MimeUtility.java b/src/com/fsck/k9/mail/internet/MimeUtility.java index e56476878..00f037108 100644 --- a/src/com/fsck/k9/mail/internet/MimeUtility.java +++ b/src/com/fsck/k9/mail/internet/MimeUtility.java @@ -991,7 +991,7 @@ public class MimeUtility { */ if (contentTransferEncoding != null) { contentTransferEncoding = - MimeUtility.getHeaderParameter(contentTransferEncoding, null); + getHeaderParameter(contentTransferEncoding, null); if (MimeUtil.ENC_QUOTED_PRINTABLE.equalsIgnoreCase(contentTransferEncoding)) { in = new QuotedPrintableInputStream(in); } else if (MimeUtil.ENC_BASE64.equalsIgnoreCase(contentTransferEncoding)) { diff --git a/src/com/fsck/k9/mail/internet/Viewable.java b/src/com/fsck/k9/mail/internet/Viewable.java index 6ec32b926..ec21b7c71 100644 --- a/src/com/fsck/k9/mail/internet/Viewable.java +++ b/src/com/fsck/k9/mail/internet/Viewable.java @@ -7,7 +7,7 @@ import java.util.List; /** * Empty marker class interface the class hierarchy used by - * {@link com.fsck.k9.mailstore.LocalMessageExtractor#extractTextAndAttachments(android.content.Context, com.fsck.k9.mail.Message)}. + * {@link MessageExtractor#getViewables(com.fsck.k9.mail.Part, java.util.List)} * * @see Viewable.Text * @see Viewable.Html @@ -20,7 +20,7 @@ public interface Viewable { * * @see com.fsck.k9.mail.internet.MessageExtractor#isPartTextualBody(com.fsck.k9.mail.Part) */ - static abstract class Textual implements Viewable { + abstract class Textual implements Viewable { private Part mPart; public Textual(Part part) { @@ -35,7 +35,7 @@ public interface Viewable { /** * Class representing a {@code text/plain} part of a message. */ - static class Text extends Textual { + class Text extends Textual { public Text(Part part) { super(part); } @@ -44,7 +44,7 @@ public interface Viewable { /** * Class representing a {@code text/html} part of a message. */ - static class Html extends Textual { + class Html extends Textual { public Html(Part part) { super(part); } @@ -58,7 +58,7 @@ public interface Viewable { * inline. *

    */ - static class MessageHeader implements Viewable { + class MessageHeader implements Viewable { private Part mContainerPart; private Message mMessage; @@ -84,7 +84,7 @@ public interface Viewable { * class. *

    */ - static class Alternative implements Viewable { + class Alternative implements Viewable { private List mText; private List mHtml; @@ -101,5 +101,4 @@ public interface Viewable { return mHtml; } } - } diff --git a/src/com/fsck/k9/mailstore/LocalMessageExtractor.java b/src/com/fsck/k9/mailstore/LocalMessageExtractor.java index f0e11902b..f5d1a9108 100644 --- a/src/com/fsck/k9/mailstore/LocalMessageExtractor.java +++ b/src/com/fsck/k9/mailstore/LocalMessageExtractor.java @@ -19,6 +19,11 @@ import java.util.Date; import java.util.List; import static com.fsck.k9.mail.internet.MimeUtility.getHeaderParameter; +import static com.fsck.k9.mail.internet.Viewable.Alternative; +import static com.fsck.k9.mail.internet.Viewable.Html; +import static com.fsck.k9.mail.internet.Viewable.MessageHeader; +import static com.fsck.k9.mail.internet.Viewable.Text; +import static com.fsck.k9.mail.internet.Viewable.Textual; class LocalMessageExtractor { private static final String TEXT_DIVIDER = @@ -58,14 +63,14 @@ class LocalMessageExtractor { StringBuilder html = new StringBuilder(); for (Viewable viewable : viewables) { - if (viewable instanceof Viewable.Textual) { + if (viewable instanceof Textual) { // This is either a text/plain or text/html part. Fill the variables 'text' and // 'html', converting between plain text and HTML as necessary. text.append(buildText(viewable, !hideDivider)); html.append(buildHtml(viewable, !hideDivider)); hideDivider = false; - } else if (viewable instanceof Viewable.MessageHeader) { - Viewable.MessageHeader header = (Viewable.MessageHeader) viewable; + } else if (viewable instanceof MessageHeader) { + MessageHeader header = (MessageHeader) viewable; Part containerPart = header.getContainerPart(); Message innerMessage = header.getMessage(); @@ -76,9 +81,9 @@ class LocalMessageExtractor { addMessageHeaderHtml(context, html, innerMessage); hideDivider = true; - } else if (viewable instanceof Viewable.Alternative) { + } else if (viewable instanceof Alternative) { // Handle multipart/alternative contents - Viewable.Alternative alternative = (Viewable.Alternative) viewable; + Alternative alternative = (Alternative) viewable; /* * We made sure at least one of text/plain or text/html is present when @@ -161,21 +166,21 @@ class LocalMessageExtractor { private static StringBuilder buildHtml(Viewable viewable, boolean prependDivider) { StringBuilder html = new StringBuilder(); - if (viewable instanceof Viewable.Textual) { - Part part = ((Viewable.Textual)viewable).getPart(); + if (viewable instanceof Textual) { + Part part = ((Textual)viewable).getPart(); addHtmlDivider(html, part, prependDivider); String t = part.getText(); if (t == null) { t = ""; - } else if (viewable instanceof Viewable.Text) { + } else if (viewable instanceof Text) { t = HtmlConverter.textToHtml(t); } html.append(t); - } else if (viewable instanceof Viewable.Alternative) { + } else if (viewable instanceof Alternative) { // That's odd - an Alternative as child of an Alternative; go ahead and try to use the // text/html child; fall-back to the text/plain part. - Viewable.Alternative alternative = (Viewable.Alternative) viewable; + Alternative alternative = (Alternative) viewable; List htmlAlternative = alternative.getHtml().isEmpty() ? alternative.getText() : alternative.getHtml(); @@ -193,21 +198,21 @@ class LocalMessageExtractor { private static StringBuilder buildText(Viewable viewable, boolean prependDivider) { StringBuilder text = new StringBuilder(); - if (viewable instanceof Viewable.Textual) { - Part part = ((Viewable.Textual)viewable).getPart(); + if (viewable instanceof Textual) { + Part part = ((Textual)viewable).getPart(); addTextDivider(text, part, prependDivider); String t = part.getText(); if (t == null) { t = ""; - } else if (viewable instanceof Viewable.Html) { + } else if (viewable instanceof Html) { t = HtmlConverter.htmlToText(t); } text.append(t); - } else if (viewable instanceof Viewable.Alternative) { + } else if (viewable instanceof Alternative) { // That's odd - an Alternative as child of an Alternative; go ahead and try to use the // text/plain child; fall-back to the text/html part. - Viewable.Alternative alternative = (Viewable.Alternative) viewable; + Alternative alternative = (Alternative) viewable; List textAlternative = alternative.getText().isEmpty() ? alternative.getHtml() : alternative.getText(); From 15a4c90f27e75473ed9b863486a54f5092ad0ac1 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Mon, 15 Dec 2014 13:20:42 +0100 Subject: [PATCH 21/29] Update javadoc --- src/com/fsck/k9/mail/Message.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/com/fsck/k9/mail/Message.java b/src/com/fsck/k9/mail/Message.java index c1b4a09a5..368b25dc2 100644 --- a/src/com/fsck/k9/mail/Message.java +++ b/src/com/fsck/k9/mail/Message.java @@ -282,14 +282,8 @@ public abstract class Message implements Part, CompositeBody { /** * Collect attachment parts of a message. - * - * @param message - * The message to collect the attachment parts from. - * * @return A list of parts regarded as attachments. - * - * @throws MessagingException - * In case of an error. + * @throws MessagingException In case of an error. */ public List collectAttachments() throws MessagingException { try { @@ -303,14 +297,8 @@ public abstract class Message implements Part, CompositeBody { /** * Collect the viewable textual parts of a message. - * - * @param message - * The message to extract the viewable parts from. - * * @return A set of viewable parts of the message. - * - * @throws MessagingException - * In case of an error. + * @throws MessagingException In case of an error. */ public Set collectTextParts() throws MessagingException { try { From c6082584947822cc57ee43d65213881fc715bef6 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Mon, 15 Dec 2014 13:46:23 +0100 Subject: [PATCH 22/29] Unused imports --- src/com/fsck/k9/crypto/CryptoHelper.java | 1 - src/com/fsck/k9/mailstore/LocalMessage.java | 3 +-- src/com/fsck/k9/view/MessageOpenPgpView.java | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/com/fsck/k9/crypto/CryptoHelper.java b/src/com/fsck/k9/crypto/CryptoHelper.java index 9bc421127..a03e6fff9 100644 --- a/src/com/fsck/k9/crypto/CryptoHelper.java +++ b/src/com/fsck/k9/crypto/CryptoHelper.java @@ -7,7 +7,6 @@ import java.util.regex.Pattern; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; -import com.fsck.k9.mail.internet.MimeUtility; public class CryptoHelper { diff --git a/src/com/fsck/k9/mailstore/LocalMessage.java b/src/com/fsck/k9/mailstore/LocalMessage.java index 05dfacec1..555254c8f 100644 --- a/src/com/fsck/k9/mailstore/LocalMessage.java +++ b/src/com/fsck/k9/mailstore/LocalMessage.java @@ -21,7 +21,6 @@ import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MimeMessage; -import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mailstore.LockableDatabase.DbCallback; import com.fsck.k9.mailstore.LockableDatabase.WrappedException; @@ -615,4 +614,4 @@ public class LocalMessage extends MimeMessage { private String getAccountUuid() { return getAccount().getUuid(); } -} \ No newline at end of file +} diff --git a/src/com/fsck/k9/view/MessageOpenPgpView.java b/src/com/fsck/k9/view/MessageOpenPgpView.java index 65bff60c8..cb9be859b 100644 --- a/src/com/fsck/k9/view/MessageOpenPgpView.java +++ b/src/com/fsck/k9/view/MessageOpenPgpView.java @@ -35,7 +35,6 @@ import com.fsck.k9.helper.IdentityHelper; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; -import com.fsck.k9.mail.internet.MimeUtility; import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpSignatureResult; From 23d9310c6146ccb9711730e3a775151eeaa8c0a5 Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 16 Dec 2014 03:32:57 +0100 Subject: [PATCH 23/29] Remove getUuid() from StoreConfig --- src/com/fsck/k9/mail/store/StoreConfig.java | 1 - src/com/fsck/k9/mailstore/LocalStore.java | 8 +------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/com/fsck/k9/mail/store/StoreConfig.java b/src/com/fsck/k9/mail/store/StoreConfig.java index 12f7a025d..d8531d978 100644 --- a/src/com/fsck/k9/mail/store/StoreConfig.java +++ b/src/com/fsck/k9/mail/store/StoreConfig.java @@ -1,7 +1,6 @@ package com.fsck.k9.mail.store; public interface StoreConfig { - String getUuid(); String getStoreUri(); String getTransportUri(); diff --git a/src/com/fsck/k9/mailstore/LocalStore.java b/src/com/fsck/k9/mailstore/LocalStore.java index b05a9efa2..46dfaa0b3 100644 --- a/src/com/fsck/k9/mailstore/LocalStore.java +++ b/src/com/fsck/k9/mailstore/LocalStore.java @@ -213,13 +213,7 @@ public class LocalStore extends Store implements Serializable { } } - /** - * Release reference to a local mail store instance. - * - * @param account - * {@link Account} instance that is used to get the local mail store instance. - */ - private static void removeInstance(StoreConfig account) { + private static void removeInstance(Account account) { String accountUuid = account.getUuid(); sLocalStores.remove(accountUuid); } From 62c5ac8e5fa56b7af3cecd7610fd12bc4c279084 Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 16 Dec 2014 04:02:54 +0100 Subject: [PATCH 24/29] Rename 'accountId' to 'accountUuid' --- src/com/fsck/k9/fragment/MessageListFragment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/fsck/k9/fragment/MessageListFragment.java b/src/com/fsck/k9/fragment/MessageListFragment.java index f697b6f2e..c14c4fc96 100644 --- a/src/com/fsck/k9/fragment/MessageListFragment.java +++ b/src/com/fsck/k9/fragment/MessageListFragment.java @@ -2483,11 +2483,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener * @see #startActivityForResult(Intent, int) */ private void displayFolderChoice(int requestCode, Folder folder, - String accountId, String lastSelectedFolderName, + String accountUuid, String lastSelectedFolderName, List messages) { Intent intent = new Intent(getActivity(), ChooseFolder.class); - intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, accountId); + intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, accountUuid); intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, lastSelectedFolderName); if (folder == null) { From d24998d5843cc8adca12499e0199f0887ae54244 Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 16 Dec 2014 05:16:09 +0100 Subject: [PATCH 25/29] Fix tests-on-jvm --- tests-on-jvm/src/android/text/TextUtils.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests-on-jvm/src/android/text/TextUtils.java diff --git a/tests-on-jvm/src/android/text/TextUtils.java b/tests-on-jvm/src/android/text/TextUtils.java new file mode 100644 index 000000000..cdc2ddc19 --- /dev/null +++ b/tests-on-jvm/src/android/text/TextUtils.java @@ -0,0 +1,7 @@ +package android.text; + +public class TextUtils { + public static boolean isEmpty(CharSequence str) { + return (str == null || str.length() == 0); + } +} From 946565347a758a0ff239b809a3c4fa6278af962e Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 16 Dec 2014 05:40:44 +0100 Subject: [PATCH 26/29] Revert adding methods to Message and Part --- src/com/fsck/k9/activity/MessageCompose.java | 29 +++++++------ .../k9/controller/MessagingController.java | 11 ++--- src/com/fsck/k9/crypto/CryptoHelper.java | 15 ++++--- src/com/fsck/k9/mail/BodyPart.java | 13 ------ src/com/fsck/k9/mail/Message.java | 41 ------------------- src/com/fsck/k9/mail/Part.java | 10 ----- .../k9/mail/internet/MessageExtractor.java | 28 +++++++++++++ .../fsck/k9/mail/internet/MimeUtility.java | 5 ++- src/com/fsck/k9/mailstore/LocalMessage.java | 8 ++-- .../k9/mailstore/LocalMessageExtractor.java | 8 ++-- src/com/fsck/k9/view/MessageOpenPgpView.java | 10 +++-- 11 files changed, 78 insertions(+), 100 deletions(-) diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index ffdc95878..db2ee4e67 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -101,6 +101,7 @@ import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Multipart; import com.fsck.k9.mail.Part; +import com.fsck.k9.mail.internet.MessageExtractor; import com.fsck.k9.mail.internet.MimeBodyPart; import com.fsck.k9.mail.internet.MimeHeader; import com.fsck.k9.mail.internet.MimeMessage; @@ -2955,10 +2956,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, if (messageFormat == MessageFormat.HTML) { - Part part = message.findFirstPartByMimeType("text/html"); + Part part = MimeUtility.findFirstPartByMimeType(message, "text/html"); if (part != null) { // Shouldn't happen if we were the one who saved it. mQuotedTextFormat = SimpleMessageFormat.HTML; - String text = part.getText(); + String text = MessageExtractor.getTextFromPart(part); if (K9.DEBUG) { Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + "."); } @@ -3021,9 +3022,9 @@ public class MessageCompose extends K9Activity implements OnClickListener, */ private void processSourceMessageText(Message message, Integer bodyOffset, Integer bodyLength, boolean viewMessageContent) throws MessagingException { - Part textPart = message.findFirstPartByMimeType("text/plain"); + Part textPart = MimeUtility.findFirstPartByMimeType(message, "text/plain"); if (textPart != null) { - String text = textPart.getText(); + String text = MessageExtractor.getTextFromPart(textPart); if (K9.DEBUG) { Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + "."); } @@ -3091,7 +3092,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, // Figure out which message format to use for the quoted text by looking if the source // message contains a text/html part. If it does, we use that. mQuotedTextFormat = - (mSourceMessage.findFirstPartByMimeType("text/html") == null) ? + (MimeUtility.findFirstPartByMimeType(mSourceMessage, "text/html") == null) ? SimpleMessageFormat.TEXT : SimpleMessageFormat.HTML; } else { mQuotedTextFormat = SimpleMessageFormat.HTML; @@ -3221,37 +3222,39 @@ public class MessageCompose extends K9Activity implements OnClickListener, Part part; if (format == SimpleMessageFormat.HTML) { // HTML takes precedence, then text. - part = message.findFirstPartByMimeType("text/html"); + part = MimeUtility.findFirstPartByMimeType(message, "text/html"); if (part != null) { if (K9.DEBUG) { Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, HTML found."); } - return part.getText(); + return MessageExtractor.getTextFromPart(part); } - part = message.findFirstPartByMimeType("text/plain"); + part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); if (part != null) { if (K9.DEBUG) { Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, text found."); } - return HtmlConverter.textToHtml(part.getText()); + String text = MessageExtractor.getTextFromPart(part); + return HtmlConverter.textToHtml(text); } } else if (format == SimpleMessageFormat.TEXT) { // Text takes precedence, then html. - part = message.findFirstPartByMimeType("text/plain"); + part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); if (part != null) { if (K9.DEBUG) { Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, text found."); } - return part.getText(); + return MessageExtractor.getTextFromPart(part); } - part = message.findFirstPartByMimeType("text/html"); + part = MimeUtility.findFirstPartByMimeType(message, "text/html"); if (part != null) { if (K9.DEBUG) { Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, HTML found."); } - return HtmlConverter.htmlToText(part.getText()); + String text = MessageExtractor.getTextFromPart(part); + return HtmlConverter.htmlToText(text); } } diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index 76c151c8a..b10d68c07 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -78,6 +78,7 @@ import com.fsck.k9.mail.PushReceiver; import com.fsck.k9.mail.Pusher; import com.fsck.k9.mail.Store; import com.fsck.k9.mail.Transport; +import com.fsck.k9.mail.internet.MessageExtractor; import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; @@ -1744,7 +1745,7 @@ public class MessagingController implements Runnable { * right now, attachments will be left for later. */ - Set viewables = message.collectTextParts(); + Set viewables = MessageExtractor.collectTextParts(message); /* * Now download the parts we're interested in storing. @@ -3197,7 +3198,7 @@ public class MessagingController implements Runnable { try { LocalStore localStore = account.getLocalStore(); - List attachments = message.collectAttachments(); + List attachments = MessageExtractor.collectAttachments(message); for (Part attachment : attachments) { attachment.setBody(null); } @@ -4244,12 +4245,12 @@ public class MessagingController implements Runnable { try { Intent msg = new Intent(Intent.ACTION_SEND); String quotedText = null; - Part part = message.findFirstPartByMimeType("text/plain"); + Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); if (part == null) { - part = message.findFirstPartByMimeType("text/html"); + part = MimeUtility.findFirstPartByMimeType(message, "text/html"); } if (part != null) { - quotedText = part.getText(); + quotedText = MessageExtractor.getTextFromPart(part); } if (quotedText != null) { msg.putExtra(Intent.EXTRA_TEXT, quotedText); diff --git a/src/com/fsck/k9/crypto/CryptoHelper.java b/src/com/fsck/k9/crypto/CryptoHelper.java index a03e6fff9..612c4ba3d 100644 --- a/src/com/fsck/k9/crypto/CryptoHelper.java +++ b/src/com/fsck/k9/crypto/CryptoHelper.java @@ -7,6 +7,9 @@ import java.util.regex.Pattern; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; +import com.fsck.k9.mail.internet.MessageExtractor; +import com.fsck.k9.mail.internet.MimeUtility; + public class CryptoHelper { @@ -31,12 +34,12 @@ public class CryptoHelper { public boolean isEncrypted(Message message) { String data = null; try { - Part part = message.findFirstPartByMimeType("text/plain"); + Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); if (part == null) { - part = message.findFirstPartByMimeType("text/html"); + part = MimeUtility.findFirstPartByMimeType(message, "text/html"); } if (part != null) { - data = part.getText(); + data = MessageExtractor.getTextFromPart(part); } } catch (MessagingException e) { // guess not... @@ -54,12 +57,12 @@ public class CryptoHelper { public boolean isSigned(Message message) { String data = null; try { - Part part = message.findFirstPartByMimeType("text/plain"); + Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); if (part == null) { - part = message.findFirstPartByMimeType("text/html"); + part = MimeUtility.findFirstPartByMimeType(message, "text/html"); } if (part != null) { - data = part.getText(); + data = MessageExtractor.getTextFromPart(part); } } catch (MessagingException e) { // guess not... diff --git a/src/com/fsck/k9/mail/BodyPart.java b/src/com/fsck/k9/mail/BodyPart.java index 884b03ae1..551866829 100644 --- a/src/com/fsck/k9/mail/BodyPart.java +++ b/src/com/fsck/k9/mail/BodyPart.java @@ -1,8 +1,5 @@ - package com.fsck.k9.mail; -import com.fsck.k9.mail.internet.MessageExtractor; -import com.fsck.k9.mail.internet.MimeUtility; public abstract class BodyPart implements Part { private Multipart mParent; @@ -16,14 +13,4 @@ public abstract class BodyPart implements Part { } public abstract void setEncoding(String encoding) throws MessagingException; - - @Override - public String getText() { - return MessageExtractor.getTextFromPart(this); - } - - @Override - public Part findFirstPartByMimeType(String mimeType) throws MessagingException { - return MimeUtility.findFirstPartByMimeType(this, mimeType); - } } diff --git a/src/com/fsck/k9/mail/Message.java b/src/com/fsck/k9/mail/Message.java index 368b25dc2..ecc35f193 100644 --- a/src/com/fsck/k9/mail/Message.java +++ b/src/com/fsck/k9/mail/Message.java @@ -2,11 +2,9 @@ package com.fsck.k9.mail; import java.io.IOException; -import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.EnumSet; -import java.util.List; import java.util.Set; import android.util.Log; @@ -14,8 +12,6 @@ import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.mail.filter.CountingOutputStream; import com.fsck.k9.mail.filter.EOLConvertingOutputStream; -import com.fsck.k9.mail.internet.MessageExtractor; -import com.fsck.k9.mail.internet.MimeUtility; public abstract class Message implements Part, CompositeBody { @@ -270,41 +266,4 @@ public abstract class Message implements Part, CompositeBody { @Override public abstract Message clone(); - @Override - public String getText() { - return MessageExtractor.getTextFromPart(this); - } - - @Override - public Part findFirstPartByMimeType(String mimeType) throws MessagingException { - return MimeUtility.findFirstPartByMimeType(this, mimeType); - } - - /** - * Collect attachment parts of a message. - * @return A list of parts regarded as attachments. - * @throws MessagingException In case of an error. - */ - public List collectAttachments() throws MessagingException { - try { - List attachments = new ArrayList(); - MessageExtractor.getViewables(this, attachments); - return attachments; - } catch (Exception e) { - throw new MessagingException("Couldn't collect attachment parts", e); - } - } - - /** - * Collect the viewable textual parts of a message. - * @return A set of viewable parts of the message. - * @throws MessagingException In case of an error. - */ - public Set collectTextParts() throws MessagingException { - try { - return MessageExtractor.getTextParts(this); - } catch (Exception e) { - throw new MessagingException("Couldn't extract viewable parts", e); - } - } } diff --git a/src/com/fsck/k9/mail/Part.java b/src/com/fsck/k9/mail/Part.java index bcf5dcc5e..4c692b724 100644 --- a/src/com/fsck/k9/mail/Part.java +++ b/src/com/fsck/k9/mail/Part.java @@ -29,16 +29,6 @@ public interface Part { void writeTo(OutputStream out) throws IOException, MessagingException; - /** - * Reads the Part's body and returns a String based on any charset conversion that needed - * to be done. Note, this does not return a text representation of HTML. - * @return a String containing the converted text in the body, or null if there was no text - * or an error during conversion. - */ - String getText(); - - Part findFirstPartByMimeType(String mimeType) throws MessagingException; - /** * Called just prior to transmission, once the type of transport is known to * be 7bit. diff --git a/src/com/fsck/k9/mail/internet/MessageExtractor.java b/src/com/fsck/k9/mail/internet/MessageExtractor.java index a4b997116..1f35c0491 100644 --- a/src/com/fsck/k9/mail/internet/MessageExtractor.java +++ b/src/com/fsck/k9/mail/internet/MessageExtractor.java @@ -199,6 +199,34 @@ public class MessageExtractor { return getParts(getViewables(part, attachments)); } + /** + * Collect attachment parts of a message. + * @return A list of parts regarded as attachments. + * @throws MessagingException In case of an error. + */ + public static List collectAttachments(Message message) throws MessagingException { + try { + List attachments = new ArrayList(); + getViewables(message, attachments); + return attachments; + } catch (Exception e) { + throw new MessagingException("Couldn't collect attachment parts", e); + } + } + + /** + * Collect the viewable textual parts of a message. + * @return A set of viewable parts of the message. + * @throws MessagingException In case of an error. + */ + public static Set collectTextParts(Message message) throws MessagingException { + try { + return getTextParts(message); + } catch (Exception e) { + throw new MessagingException("Couldn't extract viewable parts", e); + } + } + private static Message getMessageFromPart(Part part) { while (part != null) { if (part instanceof Message) diff --git a/src/com/fsck/k9/mail/internet/MimeUtility.java b/src/com/fsck/k9/mail/internet/MimeUtility.java index 00f037108..a5ac8efa7 100644 --- a/src/com/fsck/k9/mail/internet/MimeUtility.java +++ b/src/com/fsck/k9/mail/internet/MimeUtility.java @@ -16,7 +16,10 @@ import org.apache.james.mime4j.util.MimeUtil; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; +import java.util.Set; import java.util.regex.Pattern; @@ -952,7 +955,7 @@ public class MimeUtility { if (part.getBody() instanceof Multipart) { Multipart multipart = (Multipart)part.getBody(); for (BodyPart bodyPart : multipart.getBodyParts()) { - Part ret = bodyPart.findFirstPartByMimeType(mimeType); + Part ret = MimeUtility.findFirstPartByMimeType(bodyPart, mimeType); if (ret != null) { return ret; } diff --git a/src/com/fsck/k9/mailstore/LocalMessage.java b/src/com/fsck/k9/mailstore/LocalMessage.java index 555254c8f..75365d922 100644 --- a/src/com/fsck/k9/mailstore/LocalMessage.java +++ b/src/com/fsck/k9/mailstore/LocalMessage.java @@ -20,7 +20,9 @@ import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; +import com.fsck.k9.mail.internet.MessageExtractor; import com.fsck.k9.mail.internet.MimeMessage; +import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mailstore.LockableDatabase.DbCallback; import com.fsck.k9.mailstore.LockableDatabase.WrappedException; @@ -119,16 +121,16 @@ public class LocalMessage extends MimeMessage { */ public String getTextForDisplay() throws MessagingException { String text = null; // First try and fetch an HTML part. - Part part = findFirstPartByMimeType("text/html"); + Part part = MimeUtility.findFirstPartByMimeType(this, "text/html"); if (part == null) { // If that fails, try and get a text part. - part = findFirstPartByMimeType("text/plain"); + part = MimeUtility.findFirstPartByMimeType(this, "text/plain"); if (part != null && part.getBody() instanceof LocalTextBody) { text = ((LocalTextBody) part.getBody()).getBodyForDisplay(); } } else { // We successfully found an HTML part; do the necessary character set decoding. - text = part.getText(); + text = MessageExtractor.getTextFromPart(this); } return text; } diff --git a/src/com/fsck/k9/mailstore/LocalMessageExtractor.java b/src/com/fsck/k9/mailstore/LocalMessageExtractor.java index f5d1a9108..864b9132f 100644 --- a/src/com/fsck/k9/mailstore/LocalMessageExtractor.java +++ b/src/com/fsck/k9/mailstore/LocalMessageExtractor.java @@ -170,7 +170,7 @@ class LocalMessageExtractor { Part part = ((Textual)viewable).getPart(); addHtmlDivider(html, part, prependDivider); - String t = part.getText(); + String t = MessageExtractor.getTextFromPart(part); if (t == null) { t = ""; } else if (viewable instanceof Text) { @@ -202,7 +202,7 @@ class LocalMessageExtractor { Part part = ((Textual)viewable).getPart(); addTextDivider(text, part, prependDivider); - String t = part.getText(); + String t = MessageExtractor.getTextFromPart(part); if (t == null) { t = ""; } else if (viewable instanceof Html) { @@ -446,7 +446,7 @@ class LocalMessageExtractor { Body firstBody = part.getBody(); if (part.isMimeType("text/plain")) { - String bodyText = part.getText(); + String bodyText = MessageExtractor.getTextFromPart(part); if (bodyText != null) { text = bodyText; html = HtmlConverter.textToHtml(text); @@ -455,7 +455,7 @@ class LocalMessageExtractor { firstBody instanceof MimeMultipart) { MimeMultipart multipart = (MimeMultipart) firstBody; for (BodyPart bodyPart : multipart.getBodyParts()) { - String bodyText = bodyPart.getText(); + String bodyText = MessageExtractor.getTextFromPart(bodyPart); if (bodyText != null) { if (text.isEmpty() && bodyPart.isMimeType("text/plain")) { text = bodyText; diff --git a/src/com/fsck/k9/view/MessageOpenPgpView.java b/src/com/fsck/k9/view/MessageOpenPgpView.java index cb9be859b..3c0e857da 100644 --- a/src/com/fsck/k9/view/MessageOpenPgpView.java +++ b/src/com/fsck/k9/view/MessageOpenPgpView.java @@ -36,6 +36,8 @@ import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; +import com.fsck.k9.mail.internet.MessageExtractor; +import com.fsck.k9.mail.internet.MimeUtility; import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.util.OpenPgpApi; @@ -214,7 +216,7 @@ public class MessageOpenPgpView extends LinearLayout { } else { try { // check for PGP/MIME encryption - Part pgp = message.findFirstPartByMimeType("application/pgp-encrypted"); + Part pgp = MimeUtility.findFirstPartByMimeType(message, "application/pgp-encrypted"); if (pgp != null) { Toast.makeText(mContext, R.string.pgp_mime_unsupported, Toast.LENGTH_LONG) .show(); @@ -239,12 +241,12 @@ public class MessageOpenPgpView extends LinearLayout { public void run() { try { // get data String - Part part = message.findFirstPartByMimeType("text/plain"); + Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); if (part == null) { - part = message.findFirstPartByMimeType("text/html"); + part = MimeUtility.findFirstPartByMimeType(message, "text/html"); } if (part != null) { - mData = part.getText(); + mData = MessageExtractor.getTextFromPart(part); } // wait for service to be bound From 44f6a2479b5628c9d0b78da09590902d044a7616 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Tue, 16 Dec 2014 10:56:13 +0100 Subject: [PATCH 27/29] Remove reference to K9#hideTimeZone() + test --- src/com/fsck/k9/activity/MessageCompose.java | 2 +- .../k9/controller/MessagingController.java | 2 +- src/com/fsck/k9/mail/Message.java | 2 +- .../fsck/k9/mail/internet/MimeMessage.java | 9 +++--- src/com/fsck/k9/mailstore/LocalMessage.java | 2 +- tests/src/com/fsck/k9/mail/MessageTest.java | 32 +++++++++++++++++-- .../mailstore/LocalMessageExtractorTest.java | 2 +- 7 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index db2ee4e67..ff8cbc28d 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -1336,7 +1336,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, */ private MimeMessage createMessage(boolean isDraft) throws MessagingException { MimeMessage message = new MimeMessage(); - message.addSentDate(new Date()); + message.addSentDate(new Date(), K9.hideTimeZone()); Address from = new Address(mIdentity.getEmail(), mIdentity.getName()); message.setFrom(from); message.setRecipients(RecipientType.TO, getAddresses(mToView)); diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index b10d68c07..103808d85 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -2723,7 +2723,7 @@ public class MessagingController implements Runnable { long nowTime = System.currentTimeMillis(); Date nowDate = new Date(nowTime); message.setInternalDate(nowDate); - message.addSentDate(nowDate); + message.addSentDate(nowDate, K9.hideTimeZone()); message.setFrom(new Address(account.getEmail(), "K9mail internal")); localFolder.appendMessages(Collections.singletonList(message)); diff --git a/src/com/fsck/k9/mail/Message.java b/src/com/fsck/k9/mail/Message.java index ecc35f193..7815d16c9 100644 --- a/src/com/fsck/k9/mail/Message.java +++ b/src/com/fsck/k9/mail/Message.java @@ -87,7 +87,7 @@ public abstract class Message implements Part, CompositeBody { public abstract Date getSentDate(); - public abstract void setSentDate(Date sentDate) throws MessagingException; + public abstract void setSentDate(Date sentDate, boolean hideTimeZone) throws MessagingException; public abstract Address[] getRecipients(RecipientType type) throws MessagingException; diff --git a/src/com/fsck/k9/mail/internet/MimeMessage.java b/src/com/fsck/k9/mail/internet/MimeMessage.java index c58a46080..eff4ffa2e 100644 --- a/src/com/fsck/k9/mail/internet/MimeMessage.java +++ b/src/com/fsck/k9/mail/internet/MimeMessage.java @@ -33,7 +33,6 @@ import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Multipart; import com.fsck.k9.mail.Part; -import com.fsck.k9.K9; /** * An implementation of Message that stores all of it's metadata in RFC 822 and @@ -137,12 +136,12 @@ public class MimeMessage extends Message { * @param sentDate * @throws com.fsck.k9.mail.MessagingException */ - public void addSentDate(Date sentDate) throws MessagingException { + public void addSentDate(Date sentDate, boolean hideTimeZone) throws MessagingException { if (mDateFormat == null) { mDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); } - if (K9.hideTimeZone()) { + if (hideTimeZone) { mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); } @@ -151,9 +150,9 @@ public class MimeMessage extends Message { } @Override - public void setSentDate(Date sentDate) throws MessagingException { + public void setSentDate(Date sentDate, boolean hideTimeZone) throws MessagingException { removeHeader("Date"); - addSentDate(sentDate); + addSentDate(sentDate, hideTimeZone); } public void setInternalSentDate(Date sentDate) { diff --git a/src/com/fsck/k9/mailstore/LocalMessage.java b/src/com/fsck/k9/mailstore/LocalMessage.java index 75365d922..ca28ce812 100644 --- a/src/com/fsck/k9/mailstore/LocalMessage.java +++ b/src/com/fsck/k9/mailstore/LocalMessage.java @@ -157,7 +157,7 @@ public class LocalMessage extends MimeMessage { } super.setReplyTo(mReplyTo); - super.setSentDate(this.getSentDate()); + super.setSentDate(this.getSentDate(), K9.hideTimeZone()); super.setRecipients(RecipientType.TO, mTo); super.setRecipients(RecipientType.CC, mCc); super.setRecipients(RecipientType.BCC, mBcc); diff --git a/tests/src/com/fsck/k9/mail/MessageTest.java b/tests/src/com/fsck/k9/mail/MessageTest.java index 1fabfc534..70d771df4 100644 --- a/tests/src/com/fsck/k9/mail/MessageTest.java +++ b/tests/src/com/fsck/k9/mail/MessageTest.java @@ -5,6 +5,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Date; +import java.util.TimeZone; + import org.apache.commons.io.IOUtils; import org.apache.james.mime4j.codec.Base64InputStream; import org.apache.james.mime4j.util.MimeUtil; @@ -22,6 +25,11 @@ import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.TextBody; public class MessageTest extends AndroidTestCase { + @Override + public void setUp() throws Exception { + super.setUp(); + TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo")); + } private static final String EIGHT_BIT_RESULT = "From: from@example.com\r\n" @@ -263,6 +271,28 @@ public class MessageTest extends AndroidTestCase { super(); } + public void testSetSendDateSetsSentDate() throws Exception { + Message message = sampleMessage(); + final int milliseconds = 0; + Date date = new Date(milliseconds); + message.setSentDate(date, false); + Date sentDate = message.getSentDate(); + assertNotNull(sentDate); + assertEquals(milliseconds, sentDate.getTime()); + } + + public void testSetSendDateFormatsHeaderCorrectlyWithCurrentTimeZone() throws Exception { + Message message = sampleMessage(); + message.setSentDate(new Date(0), false); + assertEquals("Thu, 01 Jan 1970 09:00:00 +0900", message.getHeader("Date")[0]); + } + + public void testSetSendDateFormatsHeaderCorrectlyWithoutTimeZone() throws Exception { + Message message = sampleMessage(); + message.setSentDate(new Date(0), true); + assertEquals("Thu, 01 Jan 1970 00:00:00 +0000", message.getHeader("Date")[0]); + } + public void testMessage() throws MessagingException, IOException { MimeMessage message; ByteArrayOutputStream out; @@ -374,7 +404,5 @@ public class MessageTest extends AndroidTestCase { sb.append(Integer.toString(mMimeBoundary++)); return sb.toString(); } - } - } diff --git a/tests/src/com/fsck/k9/mailstore/LocalMessageExtractorTest.java b/tests/src/com/fsck/k9/mailstore/LocalMessageExtractorTest.java index 2fc007caf..a6b417fa5 100644 --- a/tests/src/com/fsck/k9/mailstore/LocalMessageExtractorTest.java +++ b/tests/src/com/fsck/k9/mailstore/LocalMessageExtractorTest.java @@ -120,7 +120,7 @@ public class LocalMessageExtractorTest extends AndroidTestCase { // Create message/rfc822 body MimeMessage innerMessage = new MimeMessage(); - innerMessage.addSentDate(new Date(112, 02, 17)); + innerMessage.addSentDate(new Date(112, 02, 17), false); innerMessage.setRecipients(RecipientType.TO, new Address[] { new Address("to@example.com") }); innerMessage.setSubject("Subject"); innerMessage.setFrom(new Address("from@example.com")); From 245a6330ed5c093b529e33c066f3c529cc994cf9 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Tue, 16 Dec 2014 11:45:52 +0100 Subject: [PATCH 28/29] Move logic into MessageHelper and add tests --- .../k9/controller/MessagingController.java | 5 +- src/com/fsck/k9/helper/Contacts.java | 2 +- src/com/fsck/k9/helper/MessageHelper.java | 89 ++++++++++++++++++- src/com/fsck/k9/mail/Address.java | 84 ----------------- src/com/fsck/k9/view/MessageHeader.java | 6 +- .../com/fsck/k9/helper/HtmlConverterTest.java | 2 - .../com/fsck/k9/helper/MessageHelperTest.java | 63 +++++++++++++ 7 files changed, 155 insertions(+), 96 deletions(-) create mode 100644 tests/src/com/fsck/k9/helper/MessageHelperTest.java diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index 103808d85..770742256 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -61,6 +61,7 @@ import com.fsck.k9.activity.setup.AccountSetupIncoming; import com.fsck.k9.activity.setup.AccountSetupOutgoing; import com.fsck.k9.cache.EmailProviderCache; import com.fsck.k9.helper.Contacts; +import com.fsck.k9.helper.MessageHelper; import com.fsck.k9.helper.power.TracingPowerManager; import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock; import com.fsck.k9.mail.Address; @@ -4695,7 +4696,7 @@ public class MessagingController implements Runnable { if (fromAddrs != null) { isSelf = account.isAnIdentity(fromAddrs); if (!isSelf && fromAddrs.length > 0) { - return fromAddrs[0].toFriendly(contacts).toString(); + return MessageHelper.toFriendly(fromAddrs[0], contacts).toString(); } } @@ -4705,7 +4706,7 @@ public class MessagingController implements Runnable { if (rcpts != null && rcpts.length > 0) { return context.getString(R.string.message_to_fmt, - rcpts[0].toFriendly(contacts).toString()); + MessageHelper.toFriendly(rcpts[0], contacts).toString()); } return context.getString(R.string.general_no_sender); diff --git a/src/com/fsck/k9/helper/Contacts.java b/src/com/fsck/k9/helper/Contacts.java index f5d25a912..022139f89 100644 --- a/src/com/fsck/k9/helper/Contacts.java +++ b/src/com/fsck/k9/helper/Contacts.java @@ -18,7 +18,7 @@ import java.util.List; /** * Helper class to access the contacts stored on the device. */ -public class Contacts implements Address.Lookup { +public class Contacts { /** * The order in which the search results are returned by * {@link #searchContacts(CharSequence)}. diff --git a/src/com/fsck/k9/helper/MessageHelper.java b/src/com/fsck/k9/helper/MessageHelper.java index 55caf8f66..ddc2f4a4d 100644 --- a/src/com/fsck/k9/helper/MessageHelper.java +++ b/src/com/fsck/k9/helper/MessageHelper.java @@ -1,7 +1,11 @@ package com.fsck.k9.helper; import android.content.Context; +import android.text.Spannable; +import android.text.SpannableString; import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.text.style.ForegroundColorSpan; import android.util.Log; import com.fsck.k9.Account; @@ -16,6 +20,18 @@ import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mailstore.LocalMessage; public class MessageHelper { + /** + * If the number of addresses exceeds this value the addresses aren't + * resolved to the names of Android contacts. + * + *

    + * TODO: This number was chosen arbitrarily and should be determined by + * performance tests. + *

    + * + * @see #toFriendly(Address[], com.fsck.k9.helper.Contacts) + */ + private static final int TOO_MANY_ADDRESSES = 50; private static MessageHelper sInstance; @@ -55,11 +71,11 @@ public class MessageHelper { Address[] addrs = message.getFrom(); if (addrs.length > 0 && account.isAnIdentity(addrs[0])) { - CharSequence to = Address.toFriendly(message .getRecipients(RecipientType.TO), contactHelper); + CharSequence to = toFriendly(message.getRecipients(RecipientType.TO), contactHelper); target.compareCounterparty = to.toString(); target.sender = new SpannableStringBuilder(mContext.getString(R.string.message_to_label)).append(to); } else { - target.sender = Address.toFriendly(addrs, contactHelper); + target.sender = toFriendly(addrs, contactHelper); target.compareCounterparty = target.sender.toString(); } @@ -83,11 +99,11 @@ public class MessageHelper { CharSequence displayName; if (fromAddrs.length > 0 && account.isAnIdentity(fromAddrs[0])) { - CharSequence to = Address.toFriendly(toAddrs, contactHelper); + CharSequence to = toFriendly(toAddrs, contactHelper); displayName = new SpannableStringBuilder( mContext.getString(R.string.message_to_label)).append(to); } else { - displayName = Address.toFriendly(fromAddrs, contactHelper); + displayName = toFriendly(fromAddrs, contactHelper); } return displayName; @@ -101,4 +117,69 @@ public class MessageHelper { } return false; } + + /** + * Returns the name of the contact this email address belongs to if + * the {@link Contacts contacts} parameter is not {@code null} and a + * contact is found. Otherwise the personal portion of the {@link Address} + * is returned. If that isn't available either, the email address is + * returned. + * + * @param address An {@link com.fsck.k9.mail.Address} + * @param contacts A {@link Contacts} instance or {@code null}. + * @return A "friendly" name for this {@link Address}. + */ + public static CharSequence toFriendly(Address address, Contacts contacts) { + return toFriendly(address,contacts, + K9.showCorrespondentNames(), + K9.changeContactNameColor(), + K9.getContactNameColor()); + } + + public static CharSequence toFriendly(Address[] addresses, Contacts contacts) { + if (addresses == null) { + return null; + } + + if (addresses.length >= TOO_MANY_ADDRESSES) { + // Don't look up contacts if the number of addresses is very high. + contacts = null; + } + + SpannableStringBuilder sb = new SpannableStringBuilder(); + for (int i = 0; i < addresses.length; i++) { + sb.append(toFriendly(addresses[i], contacts)); + if (i < addresses.length - 1) { + sb.append(','); + } + } + return sb; + } + + /* package, for testing */ static CharSequence toFriendly(Address address, Contacts contacts, + boolean showCorrespondentNames, + boolean changeContactNameColor, + int contactNameColor) { + if (!showCorrespondentNames) { + return address.getAddress(); + } else if (contacts != null) { + final String name = contacts.getNameForAddress(address.getAddress()); + // TODO: The results should probably be cached for performance reasons. + if (name != null) { + if (changeContactNameColor) { + final SpannableString coloredName = new SpannableString(name); + coloredName.setSpan(new ForegroundColorSpan(contactNameColor), + 0, + coloredName.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ); + return coloredName; + } else { + return name; + } + } + } + + return (!TextUtils.isEmpty(address.getPersonal())) ? address.getPersonal() : address.getAddress(); + } } diff --git a/src/com/fsck/k9/mail/Address.java b/src/com/fsck/k9/mail/Address.java index 9702c1c8d..76a4ebf45 100644 --- a/src/com/fsck/k9/mail/Address.java +++ b/src/com/fsck/k9/mail/Address.java @@ -24,25 +24,8 @@ import com.fsck.k9.K9; public class Address { - public static interface Lookup { - String getNameForAddress(String address); - } - private static final Pattern ATOM = Pattern.compile("^(?:[a-zA-Z0-9!#$%&'*+\\-/=?^_`{|}~]|\\s)+$"); - /** - * If the number of addresses exceeds this value the addresses aren't - * resolved to the names of Android contacts. - * - *

    - * TODO: This number was chosen arbitrarily and should be determined by - * performance tests. - *

    - * - * @see Address#toFriendly(Address[], com.fsck.k9.mail.Address.Lookup) - */ - private static final int TOO_MANY_ADDRESSES = 50; - /** * Immutable empty {@link Address} array */ @@ -238,73 +221,6 @@ public class Address { return sb.toString(); } - /** - * Returns either the personal portion of the Address or the address portion if the personal - * is not available. - * @return - */ - public CharSequence toFriendly() { - return toFriendly((Lookup)null); - } - - /** - * Returns the name of the contact this email address belongs to if - * the {@link Contacts contacts} parameter is not {@code null} and a - * contact is found. Otherwise the personal portion of the {@link Address} - * is returned. If that isn't available either, the email address is - * returned. - * - * @param contacts - * A {@link Contacts} instance or {@code null}. - * @return - * A "friendly" name for this {@link Address}. - */ - public CharSequence toFriendly(final Lookup contacts) { - if (!K9.showCorrespondentNames()) { - return mAddress; - - } else if (contacts != null) { - final String name = contacts.getNameForAddress(mAddress); - - // TODO: The results should probably be cached for performance reasons. - - if (name != null) { - if (K9.changeContactNameColor()) { - final SpannableString coloredName = new SpannableString(name); - coloredName.setSpan(new ForegroundColorSpan(K9.getContactNameColor()), - 0, - coloredName.length(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE - ); - return coloredName; - } else { - return name; - } - } - } - - return (!TextUtils.isEmpty(mPersonal)) ? mPersonal : mAddress; - } - - public static CharSequence toFriendly(Address[] addresses, Lookup contacts) { - if (addresses == null) { - return null; - } - - if (addresses.length >= TOO_MANY_ADDRESSES) { - // Don't look up contacts if the number of addresses is very high. - contacts = null; - } - - SpannableStringBuilder sb = new SpannableStringBuilder(); - for (int i = 0; i < addresses.length; i++) { - sb.append(addresses[i].toFriendly(contacts)); - if (i < addresses.length - 1) { - sb.append(','); - } - } - return sb; - } /** * Unpacks an address list previously packed with packAddressList() diff --git a/src/com/fsck/k9/view/MessageHeader.java b/src/com/fsck/k9/view/MessageHeader.java index 99d02ece4..67c5e130d 100644 --- a/src/com/fsck/k9/view/MessageHeader.java +++ b/src/com/fsck/k9/view/MessageHeader.java @@ -217,9 +217,9 @@ public class MessageHeader extends LinearLayout implements OnClickListener { public void populate(final Message message, final Account account) throws MessagingException { final Contacts contacts = K9.showContactName() ? mContacts : null; - final CharSequence from = Address.toFriendly(message.getFrom(), contacts); - final CharSequence to = Address.toFriendly(message.getRecipients(Message.RecipientType.TO), contacts); - final CharSequence cc = Address.toFriendly(message.getRecipients(Message.RecipientType.CC), contacts); + final CharSequence from = MessageHelper.toFriendly(message.getFrom(), contacts); + final CharSequence to = MessageHelper.toFriendly(message.getRecipients(Message.RecipientType.TO), contacts); + final CharSequence cc = MessageHelper.toFriendly(message.getRecipients(Message.RecipientType.CC), contacts); Address[] fromAddrs = message.getFrom(); Address[] toAddrs = message.getRecipients(Message.RecipientType.TO); diff --git a/tests/src/com/fsck/k9/helper/HtmlConverterTest.java b/tests/src/com/fsck/k9/helper/HtmlConverterTest.java index 32aa241ed..c79958150 100644 --- a/tests/src/com/fsck/k9/helper/HtmlConverterTest.java +++ b/tests/src/com/fsck/k9/helper/HtmlConverterTest.java @@ -1,7 +1,5 @@ package com.fsck.k9.helper; -import com.fsck.k9.helper.HtmlConverter; - import junit.framework.TestCase; import java.io.BufferedWriter; diff --git a/tests/src/com/fsck/k9/helper/MessageHelperTest.java b/tests/src/com/fsck/k9/helper/MessageHelperTest.java new file mode 100644 index 000000000..0d71784e3 --- /dev/null +++ b/tests/src/com/fsck/k9/helper/MessageHelperTest.java @@ -0,0 +1,63 @@ +package com.fsck.k9.helper; + + +import android.graphics.Color; +import android.test.AndroidTestCase; +import android.text.SpannableString; + +import com.fsck.k9.mail.Address; + +public class MessageHelperTest extends AndroidTestCase { + private Contacts contacts; + private Contacts mockContacts; + + @Override + public void setUp() throws Exception { + super.setUp(); + contacts = new Contacts(getContext()); + mockContacts = new Contacts(getContext()) { + @Override public String getNameForAddress(String address) { + if ("test@testor.com".equals(address)) { + return "Tim Testor"; + } else { + return null; + } + } + }; + } + + public void testToFriendlyShowsPersonalPartIfItExists() throws Exception { + Address address = new Address("test@testor.com", "Tim Testor"); + assertEquals("Tim Testor", MessageHelper.toFriendly(address, contacts)); + } + + public void testToFriendlyShowsEmailPartIfNoPersonalPartExists() throws Exception { + Address address = new Address("test@testor.com"); + assertEquals("test@testor.com", MessageHelper.toFriendly(address, contacts)); + } + + public void testToFriendlyArray() throws Exception { + Address address1 = new Address("test@testor.com", "Tim Testor"); + Address address2 = new Address("foo@bar.com", "Foo Bar"); + Address[] addresses = new Address[] { address1, address2 }; + assertEquals("Tim Testor,Foo Bar", MessageHelper.toFriendly(addresses, contacts).toString()); + } + + public void testToFriendlyWithContactLookup() throws Exception { + Address address = new Address("test@testor.com"); + assertEquals("Tim Testor", MessageHelper.toFriendly(address, mockContacts).toString()); + } + + public void testToFriendlyWithChangeContactColor() throws Exception { + Address address = new Address("test@testor.com"); + CharSequence friendly = MessageHelper.toFriendly(address, mockContacts, true, true, Color.RED); + assertTrue(friendly instanceof SpannableString); + assertEquals("Tim Testor", friendly.toString()); + } + + public void testToFriendlyWithoutCorrespondentNames() throws Exception { + Address address = new Address("test@testor.com", "Tim Testor"); + CharSequence friendly = MessageHelper.toFriendly(address, mockContacts, false, false, 0); + assertEquals("test@testor.com", friendly.toString()); + } +} From 231684936bb6528ab1688fe6b9cc8b5387351960 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Tue, 16 Dec 2014 12:51:52 +0100 Subject: [PATCH 29/29] break/centralize dependencies to K9 --- src/com/fsck/k9/K9.java | 31 -- src/com/fsck/k9/mail/Address.java | 11 +- src/com/fsck/k9/mail/Folder.java | 6 +- src/com/fsck/k9/mail/K9MailLib.java | 44 +++ src/com/fsck/k9/mail/Message.java | 7 +- .../fsck/k9/mail/internet/CharsetSupport.java | 4 +- .../fsck/k9/mail/internet/DecoderUtil.java | 7 +- .../k9/mail/internet/MessageExtractor.java | 6 +- .../fsck/k9/mail/ssl/KeyChainKeyManager.java | 7 +- src/com/fsck/k9/mail/ssl/LocalKeyStore.java | 8 +- .../k9/mail/ssl/TrustedSocketFactory.java | 4 +- .../k9/mail/store/ImapResponseParser.java | 2 +- src/com/fsck/k9/mail/store/ImapStore.java | 373 +++++++++--------- src/com/fsck/k9/mail/store/ImapUtility.java | 10 +- src/com/fsck/k9/mail/store/Pop3Store.java | 39 +- src/com/fsck/k9/mail/store/WebDavStore.java | 81 ++-- .../fsck/k9/mail/transport/SmtpTransport.java | 25 +- .../k9/mail/transport/WebDavTransport.java | 12 +- 18 files changed, 352 insertions(+), 325 deletions(-) create mode 100644 src/com/fsck/k9/mail/K9MailLib.java diff --git a/src/com/fsck/k9/K9.java b/src/com/fsck/k9/K9.java index b565ffc8c..cc359a6ac 100644 --- a/src/com/fsck/k9/K9.java +++ b/src/com/fsck/k9/K9.java @@ -136,37 +136,6 @@ public class K9 extends Application { */ public static boolean DEBUG = false; - /** - * Should K-9 log the conversation it has over the wire with - * SMTP servers? - */ - - public static boolean DEBUG_PROTOCOL_SMTP = true; - - /** - * Should K-9 log the conversation it has over the wire with - * IMAP servers? - */ - - public static boolean DEBUG_PROTOCOL_IMAP = true; - - - /** - * Should K-9 log the conversation it has over the wire with - * POP3 servers? - */ - - public static boolean DEBUG_PROTOCOL_POP3 = true; - - /** - * Should K-9 log the conversation it has over the wire with - * WebDAV servers? - */ - - public static boolean DEBUG_PROTOCOL_WEBDAV = true; - - - /** * If this is enabled than logging that normally hides sensitive information * like passwords will show that information. diff --git a/src/com/fsck/k9/mail/Address.java b/src/com/fsck/k9/mail/Address.java index 76a4ebf45..38a5a0f0b 100644 --- a/src/com/fsck/k9/mail/Address.java +++ b/src/com/fsck/k9/mail/Address.java @@ -11,17 +11,12 @@ import org.apache.james.mime4j.dom.address.Mailbox; import org.apache.james.mime4j.dom.address.MailboxList; import org.apache.james.mime4j.field.address.AddressBuilder; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.SpannableStringBuilder; import android.text.TextUtils; -import android.text.style.ForegroundColorSpan; import android.text.util.Rfc822Token; import android.text.util.Rfc822Tokenizer; import android.util.Log; -import com.fsck.k9.K9; - +import static com.fsck.k9.mail.K9MailLib.LOG_TAG; public class Address { private static final Pattern ATOM = Pattern.compile("^(?:[a-zA-Z0-9!#$%&'*+\\-/=?^_`{|}~]|\\s)+$"); @@ -150,12 +145,12 @@ public class Address { Mailbox mailbox = (Mailbox)address; addresses.add(new Address(mailbox.getLocalPart() + "@" + mailbox.getDomain(), mailbox.getName(), false)); } else { - Log.e(K9.LOG_TAG, "Unknown address type from Mime4J: " + Log.e(LOG_TAG, "Unknown address type from Mime4J: " + address.getClass().toString()); } } } catch (MimeException pe) { - Log.e(K9.LOG_TAG, "MimeException in Address.parse()", pe); + Log.e(LOG_TAG, "MimeException in Address.parse()", pe); //but we do an silent failover : we just use the given string as name with empty address addresses.add(new Address(null, addressList, false)); } diff --git a/src/com/fsck/k9/mail/Folder.java b/src/com/fsck/k9/mail/Folder.java index a11e7dd32..5502ef259 100644 --- a/src/com/fsck/k9/mail/Folder.java +++ b/src/com/fsck/k9/mail/Folder.java @@ -7,7 +7,7 @@ import java.util.Set; import android.util.Log; -import com.fsck.k9.K9; +import static com.fsck.k9.mail.K9MailLib.LOG_TAG; public abstract class Folder { private String status = null; @@ -146,8 +146,8 @@ public abstract class Folder { // This is causing trouble. Disabled for now. See issue 1733 //throw new RuntimeException("fetchPart() not implemented."); - if (K9.DEBUG) - Log.d(K9.LOG_TAG, "fetchPart() not implemented."); + if (K9MailLib.isDebug()) + Log.d(LOG_TAG, "fetchPart() not implemented."); } public abstract void delete(boolean recurse) throws MessagingException; diff --git a/src/com/fsck/k9/mail/K9MailLib.java b/src/com/fsck/k9/mail/K9MailLib.java new file mode 100644 index 000000000..490b5e67a --- /dev/null +++ b/src/com/fsck/k9/mail/K9MailLib.java @@ -0,0 +1,44 @@ +package com.fsck.k9.mail; + +import com.fsck.k9.K9; + +public class K9MailLib { + private K9MailLib() {} + + public static final String LOG_TAG = K9.LOG_TAG; + + public static final int PUSH_WAKE_LOCK_TIMEOUT = K9.PUSH_WAKE_LOCK_TIMEOUT; + public static final String IDENTITY_HEADER = K9.IDENTITY_HEADER; + + /** + * Should K-9 log the conversation it has over the wire with + * SMTP servers? + */ + public static boolean DEBUG_PROTOCOL_SMTP = true; + + /** + * Should K-9 log the conversation it has over the wire with + * IMAP servers? + */ + public static boolean DEBUG_PROTOCOL_IMAP = true; + + /** + * Should K-9 log the conversation it has over the wire with + * POP3 servers? + */ + public static boolean DEBUG_PROTOCOL_POP3 = true; + + /** + * Should K-9 log the conversation it has over the wire with + * WebDAV servers? + */ + public static boolean DEBUG_PROTOCOL_WEBDAV = true; + + public static boolean isDebug() { + return K9.DEBUG; + } + + public static boolean isDebugSensitive() { + return K9.DEBUG_SENSITIVE; + } +} diff --git a/src/com/fsck/k9/mail/Message.java b/src/com/fsck/k9/mail/Message.java index 7815d16c9..45c85450f 100644 --- a/src/com/fsck/k9/mail/Message.java +++ b/src/com/fsck/k9/mail/Message.java @@ -9,10 +9,11 @@ import java.util.Set; import android.util.Log; -import com.fsck.k9.K9; import com.fsck.k9.mail.filter.CountingOutputStream; import com.fsck.k9.mail.filter.EOLConvertingOutputStream; +import static com.fsck.k9.mail.K9MailLib.LOG_TAG; + public abstract class Message implements Part, CompositeBody { public enum RecipientType { @@ -231,9 +232,9 @@ public abstract class Message implements Part, CompositeBody { eolOut.flush(); return out.getCount(); } catch (IOException e) { - Log.e(K9.LOG_TAG, "Failed to calculate a message size", e); + Log.e(LOG_TAG, "Failed to calculate a message size", e); } catch (MessagingException e) { - Log.e(K9.LOG_TAG, "Failed to calculate a message size", e); + Log.e(LOG_TAG, "Failed to calculate a message size", e); } return 0; } diff --git a/src/com/fsck/k9/mail/internet/CharsetSupport.java b/src/com/fsck/k9/mail/internet/CharsetSupport.java index 5346566f3..ffd241031 100644 --- a/src/com/fsck/k9/mail/internet/CharsetSupport.java +++ b/src/com/fsck/k9/mail/internet/CharsetSupport.java @@ -2,7 +2,6 @@ package com.fsck.k9.mail.internet; import android.util.Log; -import com.fsck.k9.K9; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Part; @@ -15,6 +14,7 @@ import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.util.Locale; +import static com.fsck.k9.mail.K9MailLib.LOG_TAG; import static com.fsck.k9.mail.internet.JisSupport.SHIFT_JIS; public class CharsetSupport { @@ -111,7 +111,7 @@ public class CharsetSupport { } if (charset.matches(rule[0])) { - Log.e(K9.LOG_TAG, "I don't know how to deal with the charset " + charset + + Log.e(LOG_TAG, "I don't know how to deal with the charset " + charset + ". Falling back to " + rule[1]); charset = rule[1]; try { diff --git a/src/com/fsck/k9/mail/internet/DecoderUtil.java b/src/com/fsck/k9/mail/internet/DecoderUtil.java index 3e439ab99..d17c83bc7 100644 --- a/src/com/fsck/k9/mail/internet/DecoderUtil.java +++ b/src/com/fsck/k9/mail/internet/DecoderUtil.java @@ -2,7 +2,6 @@ package com.fsck.k9.mail.internet; import android.util.Log; -import com.fsck.k9.K9; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import java.io.ByteArrayInputStream; @@ -13,6 +12,8 @@ import org.apache.james.mime4j.codec.Base64InputStream; import org.apache.james.mime4j.codec.QuotedPrintableInputStream; import org.apache.james.mime4j.util.CharsetUtil; +import static com.fsck.k9.mail.K9MailLib.LOG_TAG; + /** * Static methods for decoding strings, byte arrays and encoded words. @@ -168,7 +169,7 @@ class DecoderUtil { } if (encodedText.isEmpty()) { - Log.w(K9.LOG_TAG, "Missing encoded text in encoded word: '" + body.substring(begin, end) + "'"); + Log.w(LOG_TAG, "Missing encoded text in encoded word: '" + body.substring(begin, end) + "'"); return null; } @@ -177,7 +178,7 @@ class DecoderUtil { } else if (encoding.equalsIgnoreCase("B")) { return DecoderUtil.decodeB(encodedText, charset); } else { - Log.w(K9.LOG_TAG, "Warning: Unknown encoding in encoded word '" + body.substring(begin, end) + "'"); + Log.w(LOG_TAG, "Warning: Unknown encoding in encoded word '" + body.substring(begin, end) + "'"); return null; } } diff --git a/src/com/fsck/k9/mail/internet/MessageExtractor.java b/src/com/fsck/k9/mail/internet/MessageExtractor.java index 1f35c0491..ba0bfa42f 100644 --- a/src/com/fsck/k9/mail/internet/MessageExtractor.java +++ b/src/com/fsck/k9/mail/internet/MessageExtractor.java @@ -2,7 +2,6 @@ package com.fsck.k9.mail.internet; import android.util.Log; -import com.fsck.k9.K9; import com.fsck.k9.mail.Body; import com.fsck.k9.mail.BodyPart; import com.fsck.k9.mail.Message; @@ -19,6 +18,7 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.fsck.k9.mail.K9MailLib.LOG_TAG; import static com.fsck.k9.mail.internet.CharsetSupport.fixupCharset; import static com.fsck.k9.mail.internet.MimeUtility.getHeaderParameter; import static com.fsck.k9.mail.internet.Viewable.Alternative; @@ -110,13 +110,13 @@ public class MessageExtractor { * If we are not able to process the body there's nothing we can do about it. Return * null and let the upper layers handle the missing content. */ - Log.e(K9.LOG_TAG, "Unable to getTextFromPart " + oom.toString()); + Log.e(LOG_TAG, "Unable to getTextFromPart " + oom.toString()); } catch (Exception e) { /* * If we are not able to process the body there's nothing we can do about it. Return * null and let the upper layers handle the missing content. */ - Log.e(K9.LOG_TAG, "Unable to getTextFromPart", e); + Log.e(LOG_TAG, "Unable to getTextFromPart", e); } return null; } diff --git a/src/com/fsck/k9/mail/ssl/KeyChainKeyManager.java b/src/com/fsck/k9/mail/ssl/KeyChainKeyManager.java index 52973e28b..273b7dd9a 100644 --- a/src/com/fsck/k9/mail/ssl/KeyChainKeyManager.java +++ b/src/com/fsck/k9/mail/ssl/KeyChainKeyManager.java @@ -20,11 +20,12 @@ import android.security.KeyChain; import android.security.KeyChainException; import android.util.Log; -import com.fsck.k9.K9; import com.fsck.k9.R; import com.fsck.k9.mail.CertificateValidationException; import com.fsck.k9.mail.MessagingException; +import static com.fsck.k9.mail.K9MailLib.LOG_TAG; + /** * For client certificate authentication! Provide private keys and certificates * during the TLS handshake using the Android 4.0 KeyChain API. @@ -207,10 +208,10 @@ class KeyChainKeyManager extends X509ExtendedKeyManager { return mAlias; } } - Log.w(K9.LOG_TAG, "Client certificate " + mAlias + " not issued by any of the requested issuers"); + Log.w(LOG_TAG, "Client certificate " + mAlias + " not issued by any of the requested issuers"); return null; } - Log.w(K9.LOG_TAG, "Client certificate " + mAlias + " does not match any of the requested key types"); + Log.w(LOG_TAG, "Client certificate " + mAlias + " does not match any of the requested key types"); return null; } } diff --git a/src/com/fsck/k9/mail/ssl/LocalKeyStore.java b/src/com/fsck/k9/mail/ssl/LocalKeyStore.java index 7133be7b0..cc0587c2c 100644 --- a/src/com/fsck/k9/mail/ssl/LocalKeyStore.java +++ b/src/com/fsck/k9/mail/ssl/LocalKeyStore.java @@ -15,7 +15,7 @@ import org.apache.commons.io.IOUtils; import android.util.Log; -import com.fsck.k9.K9; +import static com.fsck.k9.mail.K9MailLib.LOG_TAG; public class LocalKeyStore { private static final int KEY_STORE_FILE_VERSION = 1; @@ -50,7 +50,7 @@ public class LocalKeyStore { * error, presuming setKeyStoreFile(File) is called next with a * non-null File. */ - Log.w(K9.LOG_TAG, "Local key store has not been initialized"); + Log.w(LOG_TAG, "Local key store has not been initialized"); } } @@ -92,7 +92,7 @@ public class LocalKeyStore { mKeyStore = store; mKeyStoreFile = file; } catch (Exception e) { - Log.e(K9.LOG_TAG, "Failed to initialize local key store", e); + Log.e(LOG_TAG, "Failed to initialize local key store", e); // Use of the local key store is effectively disabled. mKeyStore = null; mKeyStoreFile = null; @@ -169,7 +169,7 @@ public class LocalKeyStore { } catch (KeyStoreException e) { // Ignore: most likely there was no cert. found } catch (CertificateException e) { - Log.e(K9.LOG_TAG, "Error updating the local key store file", e); + Log.e(LOG_TAG, "Error updating the local key store file", e); } } diff --git a/src/com/fsck/k9/mail/ssl/TrustedSocketFactory.java b/src/com/fsck/k9/mail/ssl/TrustedSocketFactory.java index 9d68dd205..cf74bb899 100644 --- a/src/com/fsck/k9/mail/ssl/TrustedSocketFactory.java +++ b/src/com/fsck/k9/mail/ssl/TrustedSocketFactory.java @@ -19,6 +19,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static com.fsck.k9.mail.K9MailLib.LOG_TAG; + /** * Filter and reorder list of cipher suites and TLS versions. @@ -90,7 +92,7 @@ public class TrustedSocketFactory { */ supportedProtocols = sock.getSupportedProtocols(); } catch (Exception e) { - Log.e(K9.LOG_TAG, "Error getting information about available SSL/TLS ciphers and " + + Log.e(LOG_TAG, "Error getting information about available SSL/TLS ciphers and " + "protocols", e); } diff --git a/src/com/fsck/k9/mail/store/ImapResponseParser.java b/src/com/fsck/k9/mail/store/ImapResponseParser.java index 541ff2376..b0e61fb70 100644 --- a/src/com/fsck/k9/mail/store/ImapResponseParser.java +++ b/src/com/fsck/k9/mail/store/ImapResponseParser.java @@ -293,7 +293,7 @@ class ImapResponseParser { } catch (Exception e) { // Catch everything else and save it for later. mException = e; - //Log.e(K9.LOG_TAG, "parseLiteral(): Exception in callback method", e); + //Log.e(LOG_TAG, "parseLiteral(): Exception in callback method", e); } // Check if only some of the literal data was read diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java index c5eb8ead3..ae54c870b 100644 --- a/src/com/fsck/k9/mail/store/ImapStore.java +++ b/src/com/fsck/k9/mail/store/ImapStore.java @@ -68,6 +68,7 @@ import com.fsck.k9.mail.ConnectionSecurity; import com.fsck.k9.mail.FetchProfile; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; +import com.fsck.k9.mail.K9MailLib; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessageRetrievalListener; import com.fsck.k9.mail.MessagingException; @@ -94,6 +95,10 @@ import com.jcraft.jzlib.JZlib; import com.jcraft.jzlib.ZOutputStream; import org.apache.commons.io.IOUtils; +import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_IMAP; +import static com.fsck.k9.mail.K9MailLib.LOG_TAG; +import static com.fsck.k9.mail.K9MailLib.PUSH_WAKE_LOCK_TIMEOUT; + /** *
      * TODO Need to start keeping track of UIDVALIDITY
    @@ -539,7 +544,7 @@ public class ImapStore extends RemoteStore {
                     boolean includeFolder = true;
     
                     if (response.size() > 4 || !(response.getObject(3) instanceof String)) {
    -                    Log.w(K9.LOG_TAG, "Skipping incorrectly parsed " + commandResponse +
    +                    Log.w(LOG_TAG, "Skipping incorrectly parsed " + commandResponse +
                                 " reply: " + response);
                         continue;
                     }
    @@ -548,7 +553,7 @@ public class ImapStore extends RemoteStore {
                     try {
                         decodedFolderName = decodeFolderName(response.getString(3));
                     } catch (CharacterCodingException e) {
    -                    Log.w(K9.LOG_TAG, "Folder name not correctly encoded with the UTF-7 variant " +
    +                    Log.w(LOG_TAG, "Folder name not correctly encoded with the UTF-7 variant " +
                               "as defined by RFC 3501: " + response.getString(3), e);
     
                         //TODO: Use the raw name returned by the server for all commands that require
    @@ -619,14 +624,14 @@ public class ImapStore extends RemoteStore {
             String commandOptions = "";
     
             if (connection.capabilities.contains("XLIST")) {
    -            if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration: Using XLIST.");
    +            if (K9MailLib.isDebug()) Log.d(LOG_TAG, "Folder auto-configuration: Using XLIST.");
                 commandResponse = "XLIST";
             } else if(connection.capabilities.contains("SPECIAL-USE")) {
    -            if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration: Using RFC6154/SPECIAL-USE.");
    +            if (K9MailLib.isDebug()) Log.d(LOG_TAG, "Folder auto-configuration: Using RFC6154/SPECIAL-USE.");
                 commandResponse = "LIST";
                 commandOptions = " (SPECIAL-USE)";
             } else {
    -            if (K9.DEBUG) Log.d(K9.LOG_TAG, "No detected folder auto-configuration methods.");
    +            if (K9MailLib.isDebug()) Log.d(LOG_TAG, "No detected folder auto-configuration methods.");
                 return;
             }
     
    @@ -641,7 +646,7 @@ public class ImapStore extends RemoteStore {
                     try {
                         decodedFolderName = decodeFolderName(response.getString(3));
                     } catch (CharacterCodingException e) {
    -                    Log.w(K9.LOG_TAG, "Folder name not correctly encoded with the UTF-7 variant " +
    +                    Log.w(LOG_TAG, "Folder name not correctly encoded with the UTF-7 variant " +
                             "as defined by RFC 3501: " + response.getString(3), e);
                         // We currently just skip folders with malformed names.
                         continue;
    @@ -657,17 +662,17 @@ public class ImapStore extends RemoteStore {
                         String attribute = attributes.getString(i);
                         if (attribute.equals("\\Drafts")) {
                             mStoreConfig.setDraftsFolderName(decodedFolderName);
    -                        if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected draft folder: " + decodedFolderName);
    +                        if (K9MailLib.isDebug()) Log.d(LOG_TAG, "Folder auto-configuration detected draft folder: " + decodedFolderName);
                         } else if (attribute.equals("\\Sent")) {
                             mStoreConfig.setSentFolderName(decodedFolderName);
    -                        if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected sent folder: " + decodedFolderName);
    +                        if (K9MailLib.isDebug()) Log.d(LOG_TAG, "Folder auto-configuration detected sent folder: " + decodedFolderName);
                         } else if (attribute.equals("\\Spam") || attribute.equals("\\Junk")) {
                             //rfc6154 just mentions \Junk
                             mStoreConfig.setSpamFolderName(decodedFolderName);
    -                        if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected spam folder: " + decodedFolderName);
    +                        if (K9MailLib.isDebug()) Log.d(LOG_TAG, "Folder auto-configuration detected spam folder: " + decodedFolderName);
                         } else if (attribute.equals("\\Trash")) {
                             mStoreConfig.setTrashFolderName(decodedFolderName);
    -                        if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected trash folder: " + decodedFolderName);
    +                        if (K9MailLib.isDebug()) Log.d(LOG_TAG, "Folder auto-configuration detected trash folder: " + decodedFolderName);
                         }
                     }
                 }
    @@ -909,7 +914,7 @@ public class ImapStore extends RemoteStore {
                 } catch (IOException ioe) {
                     throw ioExceptionHandler(mConnection, ioe);
                 } catch (MessagingException me) {
    -                Log.e(K9.LOG_TAG, "Unable to open connection for " + getLogId(), me);
    +                Log.e(LOG_TAG, "Unable to open connection for " + getLogId(), me);
                     throw me;
                 }
             }
    @@ -963,7 +968,7 @@ public class ImapStore extends RemoteStore {
                 synchronized (this) {
                     // If we are mid-search and we get a close request, we gotta trash the connection.
                     if (mInSearch && mConnection != null) {
    -                    Log.i(K9.LOG_TAG, "IMAP search was aborted, shutting down connection.");
    +                    Log.i(LOG_TAG, "IMAP search was aborted, shutting down connection.");
                         mConnection.close();
                     } else {
                         releaseConnection(mConnection);
    @@ -1112,8 +1117,8 @@ public class ImapStore extends RemoteStore {
                         /*
                          * If the remote folder doesn't exist we try to create it.
                          */
    -                    if (K9.DEBUG) {
    -                        Log.i(K9.LOG_TAG, "ImapFolder.copyMessages: attempting to create remote " +
    +                    if (K9MailLib.isDebug()) {
    +                        Log.i(LOG_TAG, "ImapFolder.copyMessages: attempting to create remote " +
                                     "folder '" + remoteDestName + "' for " + getLogId());
                         }
     
    @@ -1164,15 +1169,15 @@ public class ImapStore extends RemoteStore {
                                             uidMap.put(srcUid, destUid);
                                         }
                                     } else {
    -                                    if (K9.DEBUG) {
    -                                        Log.v(K9.LOG_TAG, "Parse error: size of source UIDs " +
    +                                    if (K9MailLib.isDebug()) {
    +                                        Log.v(LOG_TAG, "Parse error: size of source UIDs " +
                                                     "list is not the same as size of destination " +
                                                     "UIDs list.");
                                         }
                                     }
                                 } else {
    -                                if (K9.DEBUG) {
    -                                    Log.v(K9.LOG_TAG, "Parsing of the sequence set failed.");
    +                                if (K9MailLib.isDebug()) {
    +                                    Log.v(LOG_TAG, "Parsing of the sequence set failed.");
                                     }
                                 }
                             }
    @@ -1209,14 +1214,14 @@ public class ImapStore extends RemoteStore {
                         /*
                          * If the remote trash folder doesn't exist we try to create it.
                          */
    -                    if (K9.DEBUG)
    -                        Log.i(K9.LOG_TAG, "IMAPMessage.delete: attempting to create remote '" + trashFolderName + "' folder for " + getLogId());
    +                    if (K9MailLib.isDebug())
    +                        Log.i(LOG_TAG, "IMAPMessage.delete: attempting to create remote '" + trashFolderName + "' folder for " + getLogId());
                         remoteTrashFolder.create(FolderType.HOLDS_MESSAGES);
                     }
     
                     if (exists(remoteTrashName)) {
    -                    if (K9.DEBUG)
    -                        Log.d(K9.LOG_TAG, "IMAPMessage.delete: copying remote " + messages.size() + " messages to '" + trashFolderName + "' for " + getLogId());
    +                    if (K9MailLib.isDebug())
    +                        Log.d(LOG_TAG, "IMAPMessage.delete: copying remote " + messages.size() + " messages to '" + trashFolderName + "' for " + getLogId());
     
                         moveMessages(messages, remoteTrashFolder);
                     } else {
    @@ -1276,7 +1281,7 @@ public class ImapStore extends RemoteStore {
                         return Long.parseLong(messages.get(0).getUid());
                     }
                 } catch (Exception e) {
    -                Log.e(K9.LOG_TAG, "Unable to find highest UID in folder " + getName(), e);
    +                Log.e(LOG_TAG, "Unable to find highest UID in folder " + getName(), e);
                 }
                 return -1L;
     
    @@ -1456,7 +1461,7 @@ public class ImapStore extends RemoteStore {
                     fetchFields.add("INTERNALDATE");
                     fetchFields.add("RFC822.SIZE");
                     fetchFields.add("BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc " +
    -                        "reply-to message-id references in-reply-to " + K9.IDENTITY_HEADER + ")]");
    +                        "reply-to message-id references in-reply-to " + K9MailLib.IDENTITY_HEADER + ")]");
                 }
                 if (fp.contains(FetchProfile.Item.STRUCTURE)) {
                     fetchFields.add("BODYSTRUCTURE");
    @@ -1501,18 +1506,18 @@ public class ImapStore extends RemoteStore {
                                 if (uid != null) {
                                     try {
                                         msgSeqUidMap.put(msgSeq, uid);
    -                                    if (K9.DEBUG) {
    -                                        Log.v(K9.LOG_TAG, "Stored uid '" + uid + "' for msgSeq " + msgSeq + " into map " /*+ msgSeqUidMap.toString() */);
    +                                    if (K9MailLib.isDebug()) {
    +                                        Log.v(LOG_TAG, "Stored uid '" + uid + "' for msgSeq " + msgSeq + " into map " /*+ msgSeqUidMap.toString() */);
                                         }
                                     } catch (Exception e) {
    -                                    Log.e(K9.LOG_TAG, "Unable to store uid '" + uid + "' for msgSeq " + msgSeq);
    +                                    Log.e(LOG_TAG, "Unable to store uid '" + uid + "' for msgSeq " + msgSeq);
                                     }
                                 }
     
                                 Message message = messageMap.get(uid);
                                 if (message == null) {
    -                                if (K9.DEBUG)
    -                                    Log.d(K9.LOG_TAG, "Do not have message in messageMap for UID " + uid + " for " + getLogId());
    +                                if (K9MailLib.isDebug())
    +                                    Log.d(LOG_TAG, "Do not have message in messageMap for UID " + uid + " for " + getLogId());
     
                                     handleUntaggedResponse(response);
                                     continue;
    @@ -1591,8 +1596,8 @@ public class ImapStore extends RemoteStore {
                             String uid = fetchList.getKeyedString("UID");
     
                             if (!message.getUid().equals(uid)) {
    -                            if (K9.DEBUG)
    -                                Log.d(K9.LOG_TAG, "Did not ask for UID " + uid + " for " + getLogId());
    +                            if (K9MailLib.isDebug())
    +                                Log.d(LOG_TAG, "Did not ask for UID " + uid + " for " + getLogId());
     
                                 handleUntaggedResponse(response);
                                 continue;
    @@ -1679,8 +1684,8 @@ public class ImapStore extends RemoteStore {
                         try {
                             parseBodyStructure(bs, message, "TEXT");
                         } catch (MessagingException e) {
    -                        if (K9.DEBUG)
    -                            Log.d(K9.LOG_TAG, "Error handling message for " + getLogId(), e);
    +                        if (K9MailLib.isDebug())
    +                            Log.d(LOG_TAG, "Error handling message for " + getLogId(), e);
                             message.setBody(null);
                         }
                     }
    @@ -1728,8 +1733,8 @@ public class ImapStore extends RemoteStore {
                                 String key = (String)keyObj;
                                 if ("UIDNEXT".equalsIgnoreCase(key)) {
                                     uidNext = bracketed.getLong(1);
    -                                if (K9.DEBUG)
    -                                    Log.d(K9.LOG_TAG, "Got UidNext = " + uidNext + " for " + getLogId());
    +                                if (K9MailLib.isDebug())
    +                                    Log.d(LOG_TAG, "Got UidNext = " + uidNext + " for " + getLogId());
                                 }
                             }
                         }
    @@ -1747,15 +1752,15 @@ public class ImapStore extends RemoteStore {
                 if (response.mTag == null && response.size() > 1) {
                     if (ImapResponseParser.equalsIgnoreCase(response.get(1), "EXISTS")) {
                         mMessageCount = response.getNumber(0);
    -                    if (K9.DEBUG)
    -                        Log.d(K9.LOG_TAG, "Got untagged EXISTS with value " + mMessageCount + " for " + getLogId());
    +                    if (K9MailLib.isDebug())
    +                        Log.d(LOG_TAG, "Got untagged EXISTS with value " + mMessageCount + " for " + getLogId());
                     }
                     handlePossibleUidNext(response);
     
                     if (ImapResponseParser.equalsIgnoreCase(response.get(1), "EXPUNGE") && mMessageCount > 0) {
                         mMessageCount--;
    -                    if (K9.DEBUG)
    -                        Log.d(K9.LOG_TAG, "Got untagged EXPUNGE with mMessageCount " + mMessageCount + " for " + getLogId());
    +                    if (K9MailLib.isDebug())
    +                        Log.d(LOG_TAG, "Got untagged EXPUNGE with mMessageCount " + mMessageCount + " for " + getLogId());
                     }
     //            if (response.size() > 1) {
     //                Object bracketedObj = response.get(1);
    @@ -1777,7 +1782,7 @@ public class ImapStore extends RemoteStore {
     //                                    sb.append(' ');
     //                                }
     //
    -//                                Log.w(K9.LOG_TAG, "ALERT: " + sb.toString() + " for " + getLogId());
    +//                                Log.w(LOG_TAG, "ALERT: " + sb.toString() + " for " + getLogId());
     //                            }
     //                        }
     //                    }
    @@ -1786,7 +1791,7 @@ public class ImapStore extends RemoteStore {
     //                }
     //            }
                 }
    -            //Log.i(K9.LOG_TAG, "mMessageCount = " + mMessageCount + " for " + getLogId());
    +            //Log.i(LOG_TAG, "mMessageCount = " + mMessageCount + " for " + getLogId());
             }
     
             private void parseBodyStructure(ImapList bs, Part part, String id)
    @@ -2015,8 +2020,8 @@ public class ImapStore extends RemoteStore {
                          * not implement the APPENDUID response code.
                          */
                         String newUid = getUidFromMessageId(message);
    -                    if (K9.DEBUG) {
    -                        Log.d(K9.LOG_TAG, "Got UID " + newUid + " for message for " + getLogId());
    +                    if (K9MailLib.isDebug()) {
    +                        Log.d(LOG_TAG, "Got UID " + newUid + " for message for " + getLogId());
                         }
     
                         if (!TextUtils.isEmpty(newUid)) {
    @@ -2046,13 +2051,13 @@ public class ImapStore extends RemoteStore {
                     String[] messageIdHeader = message.getHeader("Message-ID");
     
                     if (messageIdHeader == null || messageIdHeader.length == 0) {
    -                    if (K9.DEBUG)
    -                        Log.d(K9.LOG_TAG, "Did not get a message-id in order to search for UID  for " + getLogId());
    +                    if (K9MailLib.isDebug())
    +                        Log.d(LOG_TAG, "Did not get a message-id in order to search for UID  for " + getLogId());
                         return null;
                     }
                     String messageId = messageIdHeader[0];
    -                if (K9.DEBUG)
    -                    Log.d(K9.LOG_TAG, "Looking for UID for message with message-id " + messageId + " for " + getLogId());
    +                if (K9MailLib.isDebug())
    +                    Log.d(LOG_TAG, "Looking for UID for message with message-id " + messageId + " for " + getLogId());
     
                     List responses =
                         executeSimpleCommand(
    @@ -2131,7 +2136,7 @@ public class ImapStore extends RemoteStore {
                         return null;
                     }
                 } catch (Exception e) {
    -                Log.e(K9.LOG_TAG, "Exception while updated push state for " + getLogId(), e);
    +                Log.e(LOG_TAG, "Exception while updated push state for " + getLogId(), e);
                     return null;
                 }
             }
    @@ -2163,7 +2168,7 @@ public class ImapStore extends RemoteStore {
             }
     
             private MessagingException ioExceptionHandler(ImapConnection connection, IOException ioe) {
    -            Log.e(K9.LOG_TAG, "IOException for " + getLogId(), ioe);
    +            Log.e(LOG_TAG, "IOException for " + getLogId(), ioe);
                 if (connection != null) {
                     connection.close();
                 }
    @@ -2346,14 +2351,14 @@ public class ImapStore extends RemoteStore {
     
                     if (capabilityList != null && !capabilityList.isEmpty() &&
                             ImapResponseParser.equalsIgnoreCase(capabilityList.get(0), CAPABILITY_CAPABILITY)) {
    -                    if (K9.DEBUG) {
    -                        Log.d(K9.LOG_TAG, "Saving " + capabilityList.size() + " capabilities for " + getLogId());
    +                    if (K9MailLib.isDebug()) {
    +                        Log.d(LOG_TAG, "Saving " + capabilityList.size() + " capabilities for " + getLogId());
                         }
                         for (Object capability : capabilityList) {
                             if (capability instanceof String) {
    -//                            if (K9.DEBUG)
    +//                            if (K9MailLib.isDebug())
     //                            {
    -//                                Log.v(K9.LOG_TAG, "Saving capability '" + capability + "' for " + getLogId());
    +//                                Log.v(LOG_TAG, "Saving capability '" + capability + "' for " + getLogId());
     //                            }
                                 capabilities.add(((String)capability).toUpperCase(Locale.US));
                             }
    @@ -2374,14 +2379,14 @@ public class ImapStore extends RemoteStore {
                 try {
                     Security.setProperty("networkaddress.cache.ttl", "0");
                 } catch (Exception e) {
    -                Log.w(K9.LOG_TAG, "Could not set DNS ttl to 0 for " + getLogId(), e);
    +                Log.w(LOG_TAG, "Could not set DNS ttl to 0 for " + getLogId(), e);
                 }
     
     
                 try {
                     Security.setProperty("networkaddress.cache.negative.ttl", "0");
                 } catch (Exception e) {
    -                Log.w(K9.LOG_TAG, "Could not set DNS negative ttl to 0 for " + getLogId(), e);
    +                Log.w(LOG_TAG, "Could not set DNS negative ttl to 0 for " + getLogId(), e);
                 }
     
                 try {
    @@ -2391,8 +2396,8 @@ public class ImapStore extends RemoteStore {
                     InetAddress[] addresses = InetAddress.getAllByName(mSettings.getHost());
                     for (int i = 0; i < addresses.length; i++) {
                         try {
    -                        if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP) {
    -                            Log.d(K9.LOG_TAG, "Connecting to " + mSettings.getHost() + " as " +
    +                        if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP) {
    +                            Log.d(LOG_TAG, "Connecting to " + mSettings.getHost() + " as " +
                                         addresses[i]);
                             }
     
    @@ -2428,16 +2433,16 @@ public class ImapStore extends RemoteStore {
     
                     capabilities.clear();
                     ImapResponse nullResponse = mParser.readResponse();
    -                if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP)
    -                    Log.v(K9.LOG_TAG, getLogId() + "<<<" + nullResponse);
    +                if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP)
    +                    Log.v(LOG_TAG, getLogId() + "<<<" + nullResponse);
     
                     List nullResponses = new LinkedList();
                     nullResponses.add(nullResponse);
                     receiveCapabilities(nullResponses);
     
                     if (!hasCapability(CAPABILITY_CAPABILITY)) {
    -                    if (K9.DEBUG)
    -                        Log.i(K9.LOG_TAG, "Did not get capabilities in banner, requesting CAPABILITY for " + getLogId());
    +                    if (K9MailLib.isDebug())
    +                        Log.i(LOG_TAG, "Did not get capabilities in banner, requesting CAPABILITY for " + getLogId());
                         List responses = receiveCapabilities(executeSimpleCommand(COMMAND_CAPABILITY));
                         if (responses.size() != 2) {
                             throw new MessagingException("Invalid CAPABILITY response received");
    @@ -2459,8 +2464,8 @@ public class ImapStore extends RemoteStore {
                             mParser = new ImapResponseParser(mIn);
                             mOut = new BufferedOutputStream(mSocket.getOutputStream(), 1024);
                             // Per RFC 2595 (3.1):  Once TLS has been started, reissue CAPABILITY command
    -                        if (K9.DEBUG)
    -                            Log.i(K9.LOG_TAG, "Updating capabilities after STARTTLS for " + getLogId());
    +                        if (K9MailLib.isDebug())
    +                            Log.i(LOG_TAG, "Updating capabilities after STARTTLS for " + getLogId());
                             capabilities.clear();
                             List responses = receiveCapabilities(executeSimpleCommand(COMMAND_CAPABILITY));
                             if (responses.size() != 2) {
    @@ -2514,8 +2519,8 @@ public class ImapStore extends RemoteStore {
                                 "Unhandled authentication method found in the server settings (bug).");
                     }
                     authSuccess = true;
    -                if (K9.DEBUG) {
    -                    Log.d(K9.LOG_TAG, CAPABILITY_COMPRESS_DEFLATE + " = " + hasCapability(CAPABILITY_COMPRESS_DEFLATE));
    +                if (K9MailLib.isDebug()) {
    +                    Log.d(LOG_TAG, CAPABILITY_COMPRESS_DEFLATE + " = " + hasCapability(CAPABILITY_COMPRESS_DEFLATE));
                     }
                     if (hasCapability(CAPABILITY_COMPRESS_DEFLATE)) {
                         ConnectivityManager connectivityManager = (ConnectivityManager)K9.app.getSystemService(Context.CONNECTIVITY_SERVICE);
    @@ -2524,13 +2529,13 @@ public class ImapStore extends RemoteStore {
                         NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
                         if (netInfo != null) {
                             int type = netInfo.getType();
    -                        if (K9.DEBUG)
    -                            Log.d(K9.LOG_TAG, "On network type " + type);
    +                        if (K9MailLib.isDebug())
    +                            Log.d(LOG_TAG, "On network type " + type);
                             useCompression = mSettings.useCompression(type);
     
                         }
    -                    if (K9.DEBUG)
    -                        Log.d(K9.LOG_TAG, "useCompression " + useCompression);
    +                    if (K9MailLib.isDebug())
    +                        Log.d(LOG_TAG, "useCompression " + useCompression);
                         if (useCompression) {
                             try {
                                 executeSimpleCommand(COMMAND_COMPRESS_DEFLATE);
    @@ -2541,53 +2546,53 @@ public class ImapStore extends RemoteStore {
                                 ZOutputStream zOutputStream = new ZOutputStream(mSocket.getOutputStream(), JZlib.Z_BEST_SPEED, true);
                                 mOut = new BufferedOutputStream(zOutputStream, 1024);
                                 zOutputStream.setFlushMode(JZlib.Z_PARTIAL_FLUSH);
    -                            if (K9.DEBUG) {
    -                                Log.i(K9.LOG_TAG, "Compression enabled for " + getLogId());
    +                            if (K9MailLib.isDebug()) {
    +                                Log.i(LOG_TAG, "Compression enabled for " + getLogId());
                                 }
                             } catch (Exception e) {
    -                            Log.e(K9.LOG_TAG, "Unable to negotiate compression", e);
    +                            Log.e(LOG_TAG, "Unable to negotiate compression", e);
                             }
                         }
                     }
     
     
    -                if (K9.DEBUG)
    -                    Log.d(K9.LOG_TAG, "NAMESPACE = " + hasCapability(CAPABILITY_NAMESPACE)
    +                if (K9MailLib.isDebug())
    +                    Log.d(LOG_TAG, "NAMESPACE = " + hasCapability(CAPABILITY_NAMESPACE)
                               + ", mPathPrefix = " + mSettings.getPathPrefix());
     
                     if (mSettings.getPathPrefix() == null) {
                         if (hasCapability(CAPABILITY_NAMESPACE)) {
    -                        if (K9.DEBUG)
    -                            Log.i(K9.LOG_TAG, "mPathPrefix is unset and server has NAMESPACE capability");
    +                        if (K9MailLib.isDebug())
    +                            Log.i(LOG_TAG, "mPathPrefix is unset and server has NAMESPACE capability");
                             List namespaceResponses =
                                 executeSimpleCommand(COMMAND_NAMESPACE);
                             for (ImapResponse response : namespaceResponses) {
                                 if (ImapResponseParser.equalsIgnoreCase(response.get(0), COMMAND_NAMESPACE)) {
    -                                if (K9.DEBUG)
    -                                    Log.d(K9.LOG_TAG, "Got NAMESPACE response " + response + " on " + getLogId());
    +                                if (K9MailLib.isDebug())
    +                                    Log.d(LOG_TAG, "Got NAMESPACE response " + response + " on " + getLogId());
     
                                     Object personalNamespaces = response.get(1);
                                     if (personalNamespaces != null && personalNamespaces instanceof ImapList) {
    -                                    if (K9.DEBUG)
    -                                        Log.d(K9.LOG_TAG, "Got personal namespaces: " + personalNamespaces);
    +                                    if (K9MailLib.isDebug())
    +                                        Log.d(LOG_TAG, "Got personal namespaces: " + personalNamespaces);
                                         ImapList bracketed = (ImapList)personalNamespaces;
                                         Object firstNamespace = bracketed.get(0);
                                         if (firstNamespace != null && firstNamespace instanceof ImapList) {
    -                                        if (K9.DEBUG)
    -                                            Log.d(K9.LOG_TAG, "Got first personal namespaces: " + firstNamespace);
    +                                        if (K9MailLib.isDebug())
    +                                            Log.d(LOG_TAG, "Got first personal namespaces: " + firstNamespace);
                                             bracketed = (ImapList)firstNamespace;
                                             mSettings.setPathPrefix(bracketed.getString(0));
                                             mSettings.setPathDelimeter(bracketed.getString(1));
                                             mSettings.setCombinedPrefix(null);
    -                                        if (K9.DEBUG)
    -                                            Log.d(K9.LOG_TAG, "Got path '" + mSettings.getPathPrefix() + "' and separator '" + mSettings.getPathDelimeter() + "'");
    +                                        if (K9MailLib.isDebug())
    +                                            Log.d(LOG_TAG, "Got path '" + mSettings.getPathPrefix() + "' and separator '" + mSettings.getPathDelimeter() + "'");
                                         }
                                     }
                                 }
                             }
                         } else {
    -                        if (K9.DEBUG)
    -                            Log.i(K9.LOG_TAG, "mPathPrefix is unset but server does not have NAMESPACE capability");
    +                        if (K9MailLib.isDebug())
    +                            Log.i(LOG_TAG, "mPathPrefix is unset but server does not have NAMESPACE capability");
                             mSettings.setPathPrefix("");
                         }
                     }
    @@ -2599,12 +2604,12 @@ public class ImapStore extends RemoteStore {
                                 if (ImapResponseParser.equalsIgnoreCase(response.get(0), "LIST")) {
                                     mSettings.setPathDelimeter(response.getString(2));
                                     mSettings.setCombinedPrefix(null);
    -                                if (K9.DEBUG)
    -                                    Log.d(K9.LOG_TAG, "Got path delimeter '" + mSettings.getPathDelimeter() + "' for " + getLogId());
    +                                if (K9MailLib.isDebug())
    +                                    Log.d(LOG_TAG, "Got path delimeter '" + mSettings.getPathDelimeter() + "' for " + getLogId());
                                 }
                             }
                         } catch (Exception e) {
    -                        Log.e(K9.LOG_TAG, "Unable to get path delimeter using LIST", e);
    +                        Log.e(LOG_TAG, "Unable to get path delimeter using LIST", e);
                         }
                     }
     
    @@ -2621,14 +2626,14 @@ public class ImapStore extends RemoteStore {
                     String ceMess = ce.getMessage();
                     String[] tokens = ceMess.split("-");
                     if (tokens != null && tokens.length > 1 && tokens[1] != null) {
    -                    Log.e(K9.LOG_TAG, "Stripping host/port from ConnectionException for " + getLogId(), ce);
    +                    Log.e(LOG_TAG, "Stripping host/port from ConnectionException for " + getLogId(), ce);
                         throw new ConnectException(tokens[1].trim());
                     } else {
                         throw ce;
                     }
                 } finally {
                     if (!authSuccess) {
    -                    Log.e(K9.LOG_TAG, "Failed to login, closing connection for " + getLogId());
    +                    Log.e(LOG_TAG, "Failed to login, closing connection for " + getLogId());
                         close();
                     }
                 }
    @@ -2721,7 +2726,7 @@ public class ImapStore extends RemoteStore {
                             throw new MessagingException(
                                     "Command continuation aborted: " + response);
                         } else {
    -                        Log.w(K9.LOG_TAG, "After sending tag " + tag
    +                        Log.w(LOG_TAG, "After sending tag " + tag
                                     + ", got tag response from previous command "
                                     + response + " for " + getLogId());
                         }
    @@ -2737,11 +2742,11 @@ public class ImapStore extends RemoteStore {
                 ImapResponse response;
                 do {
                     response = mParser.readResponse();
    -                if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP)
    -                    Log.v(K9.LOG_TAG, getLogId() + "<<<" + response);
    +                if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP)
    +                    Log.v(LOG_TAG, getLogId() + "<<<" + response);
     
                     if (response.mTag != null && !response.mTag.equalsIgnoreCase(tag)) {
    -                    Log.w(K9.LOG_TAG, "After sending tag " + tag + ", got tag response from previous command " + response + " for " + getLogId());
    +                    Log.w(LOG_TAG, "After sending tag " + tag + ", got tag response from previous command " + response + " for " + getLogId());
                         Iterator iter = responses.iterator();
                         while (iter.hasNext()) {
                             ImapResponse delResponse = iter.next();
    @@ -2772,8 +2777,8 @@ public class ImapStore extends RemoteStore {
             }
     
             protected boolean isIdleCapable() {
    -            if (K9.DEBUG)
    -                Log.v(K9.LOG_TAG, "Connection " + getLogId() + " has " + capabilities.size() + " capabilities");
    +            if (K9MailLib.isDebug())
    +                Log.v(LOG_TAG, "Connection " + getLogId() + " has " + capabilities.size() + " capabilities");
     
                 return capabilities.contains(CAPABILITY_IDLE);
             }
    @@ -2809,8 +2814,8 @@ public class ImapStore extends RemoteStore {
             public ImapResponse readResponse(ImapResponseParser.IImapResponseCallback callback) throws IOException {
                 try {
                     ImapResponse response = mParser.readResponse(callback);
    -                if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP)
    -                    Log.v(K9.LOG_TAG, getLogId() + "<<<" + response);
    +                if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP)
    +                    Log.v(LOG_TAG, getLogId() + "<<<" + response);
     
                     return response;
                 } catch (IOException ioe) {
    @@ -2825,8 +2830,8 @@ public class ImapStore extends RemoteStore {
                 mOut.write('\n');
                 mOut.flush();
     
    -            if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP)
    -                Log.v(K9.LOG_TAG, getLogId() + ">>> " + continuation);
    +            if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP)
    +                Log.v(LOG_TAG, getLogId() + ">>> " + continuation);
     
             }
     
    @@ -2839,12 +2844,12 @@ public class ImapStore extends RemoteStore {
                     mOut.write(commandToSend.getBytes());
                     mOut.flush();
     
    -                if (K9.DEBUG && K9.DEBUG_PROTOCOL_IMAP) {
    -                    if (sensitive && !K9.DEBUG_SENSITIVE) {
    -                        Log.v(K9.LOG_TAG, getLogId() + ">>> "
    +                if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP) {
    +                    if (sensitive && !K9MailLib.isDebugSensitive()) {
    +                        Log.v(LOG_TAG, getLogId() + ">>> "
                                   + "[Command Hidden, Enable Sensitive Debug Logging To Show]");
                         } else {
    -                        Log.v(K9.LOG_TAG, getLogId() + ">>> " + commandToSend);
    +                        Log.v(LOG_TAG, getLogId() + ">>> " + commandToSend);
                         }
                     }
     
    @@ -2874,17 +2879,17 @@ public class ImapStore extends RemoteStore {
             public List executeSimpleCommand(String command, boolean sensitive, UntaggedHandler untaggedHandler)
             throws IOException, ImapException, MessagingException {
                 String commandToLog = command;
    -            if (sensitive && !K9.DEBUG_SENSITIVE) {
    +            if (sensitive && !K9MailLib.isDebugSensitive()) {
                     commandToLog = "*sensitive*";
                 }
     
     
    -            //if (K9.DEBUG)
    -            //    Log.v(K9.LOG_TAG, "Sending IMAP command " + commandToLog + " on connection " + getLogId());
    +            //if (K9MailLib.isDebug())
    +            //    Log.v(LOG_TAG, "Sending IMAP command " + commandToLog + " on connection " + getLogId());
     
                 String tag = sendCommand(command, sensitive);
    -            //if (K9.DEBUG)
    -            //    Log.v(K9.LOG_TAG, "Sent IMAP command " + commandToLog + " with tag " + tag + " for " + getLogId());
    +            //if (K9MailLib.isDebug())
    +            //    Log.v(LOG_TAG, "Sent IMAP command " + commandToLog + " with tag " + tag + " for " + getLogId());
     
                 return readStatusResponse(tag, commandToLog, untaggedHandler);
             }
    @@ -2954,7 +2959,7 @@ public class ImapStore extends RemoteStore {
             }
             public void refresh() throws IOException, MessagingException {
                 if (idling.get()) {
    -                wakeLock.acquire(K9.PUSH_WAKE_LOCK_TIMEOUT);
    +                wakeLock.acquire(PUSH_WAKE_LOCK_TIMEOUT);
                     sendDone();
                 }
             }
    @@ -2982,9 +2987,9 @@ public class ImapStore extends RemoteStore {
                 Runnable runner = new Runnable() {
                     @Override
                     public void run() {
    -                    wakeLock.acquire(K9.PUSH_WAKE_LOCK_TIMEOUT);
    -                    if (K9.DEBUG)
    -                        Log.i(K9.LOG_TAG, "Pusher starting for " + getLogId());
    +                    wakeLock.acquire(PUSH_WAKE_LOCK_TIMEOUT);
    +                    if (K9MailLib.isDebug())
    +                        Log.i(LOG_TAG, "Pusher starting for " + getLogId());
     
                         long lastUidNext = -1L;
                         while (!stop.get()) {
    @@ -2994,10 +2999,10 @@ public class ImapStore extends RemoteStore {
                                     String pushStateS = receiver.getPushState(getName());
                                     ImapPushState pushState = ImapPushState.parse(pushStateS);
                                     oldUidNext = pushState.uidNext;
    -                                if (K9.DEBUG)
    -                                    Log.i(K9.LOG_TAG, "Got oldUidNext " + oldUidNext + " for " + getLogId());
    +                                if (K9MailLib.isDebug())
    +                                    Log.i(LOG_TAG, "Got oldUidNext " + oldUidNext + " for " + getLogId());
                                 } catch (Exception e) {
    -                                Log.e(K9.LOG_TAG, "Unable to get oldUidNext for " + getLogId(), e);
    +                                Log.e(LOG_TAG, "Unable to get oldUidNext for " + getLogId(), e);
                                 }
     
                                 /*
    @@ -3042,16 +3047,16 @@ public class ImapStore extends RemoteStore {
                                 long newUidNext = uidNext;
     
                                 if (newUidNext == -1) {
    -                                if (K9.DEBUG) {
    -                                    Log.d(K9.LOG_TAG, "uidNext is -1, using search to find highest UID");
    +                                if (K9MailLib.isDebug()) {
    +                                    Log.d(LOG_TAG, "uidNext is -1, using search to find highest UID");
                                     }
                                     long highestUid = getHighestUid();
                                     if (highestUid != -1L) {
    -                                    if (K9.DEBUG)
    -                                        Log.d(K9.LOG_TAG, "highest UID = " + highestUid);
    +                                    if (K9MailLib.isDebug())
    +                                        Log.d(LOG_TAG, "highest UID = " + highestUid);
                                         newUidNext = highestUid + 1;
    -                                    if (K9.DEBUG)
    -                                        Log.d(K9.LOG_TAG, "highest UID = " + highestUid
    +                                    if (K9MailLib.isDebug())
    +                                        Log.d(LOG_TAG, "highest UID = " + highestUid
                                                   + ", set newUidNext to " + newUidNext);
                                     }
                                 }
    @@ -3066,8 +3071,8 @@ public class ImapStore extends RemoteStore {
                                 lastUidNext = newUidNext;
                                 if (newUidNext > startUid) {
     
    -                                if (K9.DEBUG)
    -                                    Log.i(K9.LOG_TAG, "Needs sync from uid " + startUid  + " to " + newUidNext + " for " + getLogId());
    +                                if (K9MailLib.isDebug())
    +                                    Log.i(LOG_TAG, "Needs sync from uid " + startUid  + " to " + newUidNext + " for " + getLogId());
                                     List messages = new ArrayList();
                                     for (long uid = startUid; uid < newUidNext; uid++) {
                                         ImapMessage message = new ImapMessage("" + uid, ImapFolderPusher.this);
    @@ -3080,15 +3085,15 @@ public class ImapStore extends RemoteStore {
                                 } else {
                                     List untaggedResponses = null;
                                     while (!storedUntaggedResponses.isEmpty()) {
    -                                    if (K9.DEBUG)
    -                                        Log.i(K9.LOG_TAG, "Processing " + storedUntaggedResponses.size() + " untagged responses from previous commands for " + getLogId());
    +                                    if (K9MailLib.isDebug())
    +                                        Log.i(LOG_TAG, "Processing " + storedUntaggedResponses.size() + " untagged responses from previous commands for " + getLogId());
                                         untaggedResponses = new ArrayList(storedUntaggedResponses);
                                         storedUntaggedResponses.clear();
                                         processUntaggedResponses(untaggedResponses);
                                     }
     
    -                                if (K9.DEBUG)
    -                                    Log.i(K9.LOG_TAG, "About to IDLE for " + getLogId());
    +                                if (K9MailLib.isDebug())
    +                                    Log.i(LOG_TAG, "About to IDLE for " + getLogId());
     
                                     receiver.setPushActive(getName(), true);
                                     idling.set(true);
    @@ -3101,20 +3106,20 @@ public class ImapStore extends RemoteStore {
                                     idleFailureCount.set(0);
                                 }
                             } catch (Exception e) {
    -                            wakeLock.acquire(K9.PUSH_WAKE_LOCK_TIMEOUT);
    +                            wakeLock.acquire(PUSH_WAKE_LOCK_TIMEOUT);
                                 storedUntaggedResponses.clear();
                                 idling.set(false);
                                 receiver.setPushActive(getName(), false);
                                 try {
                                     close();
                                 } catch (Exception me) {
    -                                Log.e(K9.LOG_TAG, "Got exception while closing for exception for " + getLogId(), me);
    +                                Log.e(LOG_TAG, "Got exception while closing for exception for " + getLogId(), me);
                                 }
                                 if (stop.get()) {
    -                                Log.i(K9.LOG_TAG, "Got exception while idling, but stop is set for " + getLogId());
    +                                Log.i(LOG_TAG, "Got exception while idling, but stop is set for " + getLogId());
                                 } else {
                                     receiver.pushError("Push error for " + getName(), e);
    -                                Log.e(K9.LOG_TAG, "Got exception while idling for " + getLogId(), e);
    +                                Log.e(LOG_TAG, "Got exception while idling for " + getLogId(), e);
                                     int delayTimeInt = delayTime.get();
                                     receiver.sleep(wakeLock, delayTimeInt);
                                     delayTimeInt *= 2;
    @@ -3123,7 +3128,7 @@ public class ImapStore extends RemoteStore {
                                     }
                                     delayTime.set(delayTimeInt);
                                     if (idleFailureCount.incrementAndGet() > IDLE_FAILURE_COUNT_LIMIT) {
    -                                    Log.e(K9.LOG_TAG, "Disabling pusher for " + getLogId() + " after " + idleFailureCount.get() + " consecutive errors");
    +                                    Log.e(LOG_TAG, "Disabling pusher for " + getLogId() + " after " + idleFailureCount.get() + " consecutive errors");
                                         receiver.pushError("Push disabled for " + getName() + " after " + idleFailureCount.get() + " consecutive errors", e);
                                         stop.set(true);
                                     }
    @@ -3133,11 +3138,11 @@ public class ImapStore extends RemoteStore {
                         }
                         receiver.setPushActive(getName(), false);
                         try {
    -                        if (K9.DEBUG)
    -                            Log.i(K9.LOG_TAG, "Pusher for " + getLogId() + " is exiting");
    +                        if (K9MailLib.isDebug())
    +                            Log.i(LOG_TAG, "Pusher for " + getLogId() + " is exiting");
                             close();
                         } catch (Exception me) {
    -                        Log.e(K9.LOG_TAG, "Got exception while closing for " + getLogId(), me);
    +                        Log.e(LOG_TAG, "Got exception while closing for " + getLogId(), me);
                         } finally {
                             wakeLock.release();
                         }
    @@ -3154,8 +3159,8 @@ public class ImapStore extends RemoteStore {
                     if (ImapResponseParser.equalsIgnoreCase(responseType, "FETCH")
                             || ImapResponseParser.equalsIgnoreCase(responseType, "EXPUNGE")
                             || ImapResponseParser.equalsIgnoreCase(responseType, "EXISTS")) {
    -                    if (K9.DEBUG)
    -                        Log.d(K9.LOG_TAG, "Storing response " + response + " for later processing");
    +                    if (K9MailLib.isDebug())
    +                        Log.d(LOG_TAG, "Storing response " + response + " for later processing");
     
                         storedUntaggedResponses.add(response);
                     }
    @@ -3183,8 +3188,8 @@ public class ImapStore extends RemoteStore {
                         syncMessages(mMessageCount, true);
                     }
                 }
    -            if (K9.DEBUG)
    -                Log.d(K9.LOG_TAG, "UIDs for messages needing flag sync are " + flagSyncMsgSeqs + "  for " + getLogId());
    +            if (K9MailLib.isDebug())
    +                Log.d(LOG_TAG, "UIDs for messages needing flag sync are " + flagSyncMsgSeqs + "  for " + getLogId());
     
                 if (!flagSyncMsgSeqs.isEmpty()) {
                     syncMessages(flagSyncMsgSeqs);
    @@ -3200,17 +3205,17 @@ public class ImapStore extends RemoteStore {
                     String pushStateS = receiver.getPushState(getName());
                     ImapPushState pushState = ImapPushState.parse(pushStateS);
                     oldUidNext = pushState.uidNext;
    -                if (K9.DEBUG)
    -                    Log.i(K9.LOG_TAG, "Got oldUidNext " + oldUidNext + " for " + getLogId());
    +                if (K9MailLib.isDebug())
    +                    Log.i(LOG_TAG, "Got oldUidNext " + oldUidNext + " for " + getLogId());
                 } catch (Exception e) {
    -                Log.e(K9.LOG_TAG, "Unable to get oldUidNext for " + getLogId(), e);
    +                Log.e(LOG_TAG, "Unable to get oldUidNext for " + getLogId(), e);
                 }
     
                 List messageList = getMessages(end, end, null, true, null);
                 if (messageList != null && messageList.size() > 0) {
                     long newUid = Long.parseLong(messageList.get(0).getUid());
    -                if (K9.DEBUG)
    -                    Log.i(K9.LOG_TAG, "Got newUid " + newUid + " for message " + end + " on " + getLogId());
    +                if (K9MailLib.isDebug())
    +                    Log.i(LOG_TAG, "Got newUid " + newUid + " for message " + end + " on " + getLogId());
                     long startUid = oldUidNext;
                     if (startUid < newUid - 10) {
                         startUid = newUid - 10;
    @@ -3220,8 +3225,8 @@ public class ImapStore extends RemoteStore {
                     }
                     if (newUid >= startUid) {
     
    -                    if (K9.DEBUG)
    -                        Log.i(K9.LOG_TAG, "Needs sync from uid " + startUid  + " to " + newUid + " for " + getLogId());
    +                    if (K9MailLib.isDebug())
    +                        Log.i(LOG_TAG, "Needs sync from uid " + startUid  + " to " + newUid + " for " + getLogId());
                         List messages = new ArrayList();
                         for (long uid = startUid; uid <= newUid; uid++) {
                             ImapMessage message = new ImapMessage(Long.toString(uid), ImapFolderPusher.this);
    @@ -3256,7 +3261,7 @@ public class ImapStore extends RemoteStore {
                         needsPoll.set(true);
                         msgSeqUidMap.clear();
                         String existingUid = existingMessage.getUid();
    -                    Log.w(K9.LOG_TAG, "Message with UID " + existingUid + " still exists on server, not expunging");
    +                    Log.w(LOG_TAG, "Message with UID " + existingUid + " still exists on server, not expunging");
                         removeUids.remove(existingUid);
                     }
                     for (String uid : removeUids) {
    @@ -3264,13 +3269,13 @@ public class ImapStore extends RemoteStore {
                         try {
                             message.setFlagInternal(Flag.DELETED, true);
                         } catch (MessagingException me) {
    -                        Log.e(K9.LOG_TAG, "Unable to set DELETED flag on message " + message.getUid());
    +                        Log.e(LOG_TAG, "Unable to set DELETED flag on message " + message.getUid());
                         }
                         messages.add(message);
                     }
                     receiver.messagesRemoved(this, messages);
                 } catch (Exception e) {
    -                Log.e(K9.LOG_TAG, "Cannot remove EXPUNGEd messages", e);
    +                Log.e(LOG_TAG, "Cannot remove EXPUNGEd messages", e);
                 }
     
             }
    @@ -3282,11 +3287,11 @@ public class ImapStore extends RemoteStore {
                     try {
                         Object responseType = response.get(1);
                         if (ImapResponseParser.equalsIgnoreCase(responseType, "FETCH")) {
    -                        Log.i(K9.LOG_TAG, "Got FETCH " + response);
    +                        Log.i(LOG_TAG, "Got FETCH " + response);
                             long msgSeq = response.getLong(0);
     
    -                        if (K9.DEBUG)
    -                            Log.d(K9.LOG_TAG, "Got untagged FETCH for msgseq " + msgSeq + " for " + getLogId());
    +                        if (K9MailLib.isDebug())
    +                            Log.d(LOG_TAG, "Got untagged FETCH for msgseq " + msgSeq + " for " + getLogId());
     
                             if (!flagSyncMsgSeqs.contains(msgSeq)) {
                                 flagSyncMsgSeqs.add(msgSeq);
    @@ -3297,8 +3302,8 @@ public class ImapStore extends RemoteStore {
                             if (msgSeq <= oldMessageCount) {
                                 messageCountDelta = -1;
                             }
    -                        if (K9.DEBUG)
    -                            Log.d(K9.LOG_TAG, "Got untagged EXPUNGE for msgseq " + msgSeq + " for " + getLogId());
    +                        if (K9MailLib.isDebug())
    +                            Log.d(LOG_TAG, "Got untagged EXPUNGE for msgseq " + msgSeq + " for " + getLogId());
     
                             List newSeqs = new ArrayList();
                             Iterator flagIter = flagSyncMsgSeqs.iterator();
    @@ -3318,20 +3323,20 @@ public class ImapStore extends RemoteStore {
                             Collections.sort(msgSeqs);  // Have to do comparisons in order because of msgSeq reductions
     
                             for (long msgSeqNum : msgSeqs) {
    -                            if (K9.DEBUG) {
    -                                Log.v(K9.LOG_TAG, "Comparing EXPUNGEd msgSeq " + msgSeq + " to " + msgSeqNum);
    +                            if (K9MailLib.isDebug()) {
    +                                Log.v(LOG_TAG, "Comparing EXPUNGEd msgSeq " + msgSeq + " to " + msgSeqNum);
                                 }
                                 if (msgSeqNum == msgSeq) {
                                     String uid = msgSeqUidMap.get(msgSeqNum);
    -                                if (K9.DEBUG) {
    -                                    Log.d(K9.LOG_TAG, "Scheduling removal of UID " + uid + " because msgSeq " + msgSeqNum + " was expunged");
    +                                if (K9MailLib.isDebug()) {
    +                                    Log.d(LOG_TAG, "Scheduling removal of UID " + uid + " because msgSeq " + msgSeqNum + " was expunged");
                                     }
                                     removeMsgUids.add(uid);
                                     msgSeqUidMap.remove(msgSeqNum);
                                 } else if (msgSeqNum > msgSeq) {
                                     String uid = msgSeqUidMap.get(msgSeqNum);
    -                                if (K9.DEBUG) {
    -                                    Log.d(K9.LOG_TAG, "Reducing msgSeq for UID " + uid + " from " + msgSeqNum + " to " + (msgSeqNum - 1));
    +                                if (K9MailLib.isDebug()) {
    +                                    Log.d(LOG_TAG, "Reducing msgSeq for UID " + uid + " from " + msgSeqNum + " to " + (msgSeqNum - 1));
                                     }
                                     msgSeqUidMap.remove(msgSeqNum);
                                     msgSeqUidMap.put(msgSeqNum - 1, uid);
    @@ -3339,7 +3344,7 @@ public class ImapStore extends RemoteStore {
                             }
                         }
                     } catch (Exception e) {
    -                    Log.e(K9.LOG_TAG, "Could not handle untagged FETCH for " + getLogId(), e);
    +                    Log.e(LOG_TAG, "Could not handle untagged FETCH for " + getLogId(), e);
                     }
                 }
                 return messageCountDelta;
    @@ -3370,27 +3375,27 @@ public class ImapStore extends RemoteStore {
                 }
                 ImapConnection conn = mConnection;
                 if (conn != null) {
    -                if (K9.DEBUG)
    -                    Log.v(K9.LOG_TAG, "Closing mConnection to stop pushing for " + getLogId());
    +                if (K9MailLib.isDebug())
    +                    Log.v(LOG_TAG, "Closing mConnection to stop pushing for " + getLogId());
                     conn.close();
                 } else {
    -                Log.w(K9.LOG_TAG, "Attempt to interrupt null mConnection to stop pushing on folderPusher for " + getLogId());
    +                Log.w(LOG_TAG, "Attempt to interrupt null mConnection to stop pushing on folderPusher for " + getLogId());
                 }
             }
     
             @Override
             public void handleAsyncUntaggedResponse(ImapResponse response) {
    -            if (K9.DEBUG)
    -                Log.v(K9.LOG_TAG, "Got async response: " + response);
    +            if (K9MailLib.isDebug())
    +                Log.v(LOG_TAG, "Got async response: " + response);
     
                 if (stop.get()) {
    -                if (K9.DEBUG)
    -                    Log.d(K9.LOG_TAG, "Got async untagged response: " + response + ", but stop is set for " + getLogId());
    +                if (K9MailLib.isDebug())
    +                    Log.d(LOG_TAG, "Got async untagged response: " + response + ", but stop is set for " + getLogId());
     
                     try {
                         sendDone();
                     } catch (Exception e) {
    -                    Log.e(K9.LOG_TAG, "Exception while sending DONE for " + getLogId(), e);
    +                    Log.e(LOG_TAG, "Exception while sending DONE for " + getLogId(), e);
                     }
                 } else {
                     if (response.mTag == null) {
    @@ -3400,22 +3405,22 @@ public class ImapStore extends RemoteStore {
                             if (ImapResponseParser.equalsIgnoreCase(responseType, "EXISTS") || ImapResponseParser.equalsIgnoreCase(responseType, "EXPUNGE") ||
                                     ImapResponseParser.equalsIgnoreCase(responseType, "FETCH")) {
                                 if (!started) {
    -                                wakeLock.acquire(K9.PUSH_WAKE_LOCK_TIMEOUT);
    +                                wakeLock.acquire(PUSH_WAKE_LOCK_TIMEOUT);
                                     started = true;
                                 }
     
    -                            if (K9.DEBUG)
    -                                Log.d(K9.LOG_TAG, "Got useful async untagged response: " + response + " for " + getLogId());
    +                            if (K9MailLib.isDebug())
    +                                Log.d(LOG_TAG, "Got useful async untagged response: " + response + " for " + getLogId());
     
                                 try {
                                     sendDone();
                                 } catch (Exception e) {
    -                                Log.e(K9.LOG_TAG, "Exception while sending DONE for " + getLogId(), e);
    +                                Log.e(LOG_TAG, "Exception while sending DONE for " + getLogId(), e);
                                 }
                             }
                         } else if (response.mCommandContinuationRequested) {
    -                        if (K9.DEBUG)
    -                            Log.d(K9.LOG_TAG, "Idling " + getLogId());
    +                        if (K9MailLib.isDebug())
    +                            Log.d(LOG_TAG, "Idling " + getLogId());
     
                             wakeLock.release();
                         }
    @@ -3463,7 +3468,7 @@ public class ImapStore extends RemoteStore {
                         try {
                             folderPusher.refresh();
                         } catch (Exception e) {
    -                        Log.e(K9.LOG_TAG, "Got exception while refreshing for " + folderPusher.getName(), e);
    +                        Log.e(LOG_TAG, "Got exception while refreshing for " + folderPusher.getName(), e);
                         }
                     }
                 }
    @@ -3471,17 +3476,17 @@ public class ImapStore extends RemoteStore {
     
             @Override
             public void stop() {
    -            if (K9.DEBUG)
    -                Log.i(K9.LOG_TAG, "Requested stop of IMAP pusher");
    +            if (K9MailLib.isDebug())
    +                Log.i(LOG_TAG, "Requested stop of IMAP pusher");
     
                 synchronized (folderPushers) {
                     for (ImapFolderPusher folderPusher : folderPushers.values()) {
                         try {
    -                        if (K9.DEBUG)
    -                            Log.i(K9.LOG_TAG, "Requesting stop of IMAP folderPusher " + folderPusher.getName());
    +                        if (K9MailLib.isDebug())
    +                            Log.i(LOG_TAG, "Requesting stop of IMAP folderPusher " + folderPusher.getName());
                             folderPusher.stop();
                         } catch (Exception e) {
    -                        Log.e(K9.LOG_TAG, "Got exception while stopping " + folderPusher.getName(), e);
    +                        Log.e(LOG_TAG, "Got exception while stopping " + folderPusher.getName(), e);
                         }
                     }
                     folderPushers.clear();
    @@ -3527,7 +3532,7 @@ public class ImapStore extends RemoteStore {
                                 try {
                                     newUidNext = Long.parseLong(value);
                                 } catch (NumberFormatException e) {
    -                                Log.e(K9.LOG_TAG, "Unable to part uidNext value " + value, e);
    +                                Log.e(LOG_TAG, "Unable to part uidNext value " + value, e);
                                 }
     
                             }
    diff --git a/src/com/fsck/k9/mail/store/ImapUtility.java b/src/com/fsck/k9/mail/store/ImapUtility.java
    index 971f098cd..bd3b12a74 100644
    --- a/src/com/fsck/k9/mail/store/ImapUtility.java
    +++ b/src/com/fsck/k9/mail/store/ImapUtility.java
    @@ -19,11 +19,11 @@ package com.fsck.k9.mail.store;
     
     import android.util.Log;
     
    -import com.fsck.k9.K9;
    -
     import java.util.ArrayList;
     import java.util.List;
     
    +import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
    +
     /**
      * Utility methods for use with IMAP.
      */
    @@ -101,12 +101,12 @@ class ImapUtility {
                                 }
                             }
                         } else {
    -                        Log.d(K9.LOG_TAG, "Invalid range: " + range);
    +                        Log.d(LOG_TAG, "Invalid range: " + range);
                         }
                     }
                 }
             } catch (NumberFormatException e) {
    -            Log.d(K9.LOG_TAG, "Invalid range value: " + range, e);
    +            Log.d(LOG_TAG, "Invalid range value: " + range, e);
             }
     
             return list;
    @@ -122,7 +122,7 @@ class ImapUtility {
                 // do nothing
             }
     
    -        Log.d(K9.LOG_TAG, "Invalid UID value: " + number);
    +        Log.d(LOG_TAG, "Invalid UID value: " + number);
     
             return false;
         }
    diff --git a/src/com/fsck/k9/mail/store/Pop3Store.java b/src/com/fsck/k9/mail/store/Pop3Store.java
    index b2d687fce..73366e4ae 100644
    --- a/src/com/fsck/k9/mail/store/Pop3Store.java
    +++ b/src/com/fsck/k9/mail/store/Pop3Store.java
    @@ -31,6 +31,9 @@ import java.util.Locale;
     import java.util.Map;
     import java.util.Set;
     
    +import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_POP3;
    +import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
    +
     public class Pop3Store extends RemoteStore {
         public static final String STORE_TYPE = "POP3";
     
    @@ -631,7 +634,7 @@ public class Pop3Store extends RemoteStore {
                             // response = "+OK msgNum msgUid"
                             String[] uidParts = response.split(" +");
                             if (uidParts.length < 3 || !"+OK".equals(uidParts[0])) {
    -                            Log.e(K9.LOG_TAG, "ERR response: " + response);
    +                            Log.e(LOG_TAG, "ERR response: " + response);
                                 return;
                             }
                             String msgUid = uidParts[2];
    @@ -689,8 +692,8 @@ public class Pop3Store extends RemoteStore {
                 Set unindexedUids = new HashSet();
                 for (String uid : uids) {
                     if (mUidToMsgMap.get(uid) == null) {
    -                    if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3) {
    -                        Log.d(K9.LOG_TAG, "Need to index UID " + uid);
    +                    if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) {
    +                        Log.d(LOG_TAG, "Need to index UID " + uid);
                         }
                         unindexedUids.add(uid);
                     }
    @@ -715,8 +718,8 @@ public class Pop3Store extends RemoteStore {
                         Integer msgNum = Integer.valueOf(uidParts[0]);
                         String msgUid = uidParts[1];
                         if (unindexedUids.contains(msgUid)) {
    -                        if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3) {
    -                            Log.d(K9.LOG_TAG, "Got msgNum " + msgNum + " for UID " + msgUid);
    +                        if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) {
    +                            Log.d(LOG_TAG, "Got msgNum " + msgNum + " for UID " + msgUid);
                             }
     
                             Pop3Message message = mUidToMsgMap.get(msgUid);
    @@ -730,8 +733,8 @@ public class Pop3Store extends RemoteStore {
             }
     
             private void indexMessage(int msgNum, Pop3Message message) {
    -            if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3) {
    -                Log.d(K9.LOG_TAG, "Adding index for UID " + message.getUid() + " to msgNum " + msgNum);
    +            if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) {
    +                Log.d(LOG_TAG, "Adding index for UID " + message.getUid() + " to msgNum " + msgNum);
                 }
                 mMsgNumToMsgMap.put(msgNum, message);
                 mUidToMsgMap.put(message.getUid(), message);
    @@ -902,8 +905,8 @@ public class Pop3Store extends RemoteStore {
                 // Try hard to use the TOP command if we're not asked to download the whole message.
                 if (lines != -1 && (!mTopNotSupported || mCapabilities.top)) {
                     try {
    -                    if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3 && !mCapabilities.top) {
    -                        Log.d(K9.LOG_TAG, "This server doesn't support the CAPA command. " +
    +                    if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3 && !mCapabilities.top) {
    +                        Log.d(LOG_TAG, "This server doesn't support the CAPA command. " +
                                   "Checking to see if the TOP command is supported nevertheless.");
                         }
     
    @@ -917,8 +920,8 @@ public class Pop3Store extends RemoteStore {
                             // The TOP command should be supported but something went wrong.
                             throw e;
                         } else {
    -                        if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3) {
    -                            Log.d(K9.LOG_TAG, "The server really doesn't support the TOP " +
    +                        if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) {
    +                            Log.d(LOG_TAG, "The server really doesn't support the TOP " +
                                       "command. Using RETR instead.");
                             }
     
    @@ -1025,8 +1028,8 @@ public class Pop3Store extends RemoteStore {
                     }
                 } while ((d = mIn.read()) != -1);
                 String ret = sb.toString();
    -            if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3) {
    -                Log.d(K9.LOG_TAG, "<<< " + ret);
    +            if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) {
    +                Log.d(LOG_TAG, "<<< " + ret);
                 }
                 return ret;
             }
    @@ -1118,12 +1121,12 @@ public class Pop3Store extends RemoteStore {
                     open(Folder.OPEN_MODE_RW);
     
                     if (command != null) {
    -                    if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3) {
    -                        if (sensitive && !K9.DEBUG_SENSITIVE) {
    -                            Log.d(K9.LOG_TAG, ">>> "
    +                    if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) {
    +                        if (sensitive && !K9MailLib.isDebugSensitive()) {
    +                            Log.d(LOG_TAG, ">>> "
                                       + "[Command Hidden, Enable Sensitive Debug Logging To Show]");
                             } else {
    -                            Log.d(K9.LOG_TAG, ">>> " + command);
    +                            Log.d(LOG_TAG, ">>> " + command);
                             }
                         }
     
    @@ -1195,7 +1198,7 @@ public class Pop3Store extends RemoteStore {
                 //   }
     //         catch (MessagingException me)
     //         {
    -//          Log.w(K9.LOG_TAG, "Could not delete non-existent message", me);
    +//          Log.w(LOG_TAG, "Could not delete non-existent message", me);
     //         }
             }
         }
    diff --git a/src/com/fsck/k9/mail/store/WebDavStore.java b/src/com/fsck/k9/mail/store/WebDavStore.java
    index f1202ff45..9944c6a9e 100644
    --- a/src/com/fsck/k9/mail/store/WebDavStore.java
    +++ b/src/com/fsck/k9/mail/store/WebDavStore.java
    @@ -2,8 +2,6 @@ package com.fsck.k9.mail.store;
     
     import android.util.Log;
     
    -import com.fsck.k9.K9;
    -
     import com.fsck.k9.mail.*;
     import com.fsck.k9.mail.filter.Base64;
     import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
    @@ -47,6 +45,9 @@ import java.text.SimpleDateFormat;
     import java.util.*;
     import java.util.zip.GZIPInputStream;
     
    +import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_WEBDAV;
    +import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
    +
     /**
      * 
      * Uses WebDAV formatted HTTP calls to an MS Exchange server to fetch email
    @@ -719,7 +720,7 @@ public class WebDavStore extends RemoteStore {
                     doFBA(null);
                 }
             } catch (IOException ioe) {
    -            Log.e(K9.LOG_TAG, "Error during authentication: " + ioe + "\nStack: " + processException(ioe));
    +            Log.e(LOG_TAG, "Error during authentication: " + ioe + "\nStack: " + processException(ioe));
                 throw new MessagingException("Error during authentication", ioe);
             }
     
    @@ -789,7 +790,7 @@ public class WebDavStore extends RemoteStore {
             } catch (SSLException e) {
                 throw new CertificateValidationException(e.getMessage(), e);
             } catch (IOException ioe) {
    -            Log.e(K9.LOG_TAG, "IOException: " + ioe + "\nTrace: " + processException(ioe));
    +            Log.e(LOG_TAG, "IOException: " + ioe + "\nTrace: " + processException(ioe));
                 throw new MessagingException("IOException", ioe);
             }
     
    @@ -891,7 +892,7 @@ public class WebDavStore extends RemoteStore {
                         response = httpClient.executeOverride(request, mContext);
                         authenticated = testAuthenticationResponse(response);
                     } catch (URISyntaxException e) {
    -                    Log.e(K9.LOG_TAG, "URISyntaxException caught " + e + "\nTrace: " + processException(e));
    +                    Log.e(LOG_TAG, "URISyntaxException caught " + e + "\nTrace: " + processException(e));
                         throw new MessagingException("URISyntaxException caught", e);
                     }
                 } else {
    @@ -985,7 +986,7 @@ public class WebDavStore extends RemoteStore {
                             }
                         }
                     } catch (URISyntaxException e) {
    -                    Log.e(K9.LOG_TAG, "URISyntaxException caught " + e + "\nTrace: " + processException(e));
    +                    Log.e(LOG_TAG, "URISyntaxException caught " + e + "\nTrace: " + processException(e));
                         throw new MessagingException("URISyntaxException caught", e);
                     }
                 }
    @@ -1022,10 +1023,10 @@ public class WebDavStore extends RemoteStore {
                     Scheme s = new Scheme("https", new WebDavSocketFactory(mHost, 443), 443);
                     reg.register(s);
                 } catch (NoSuchAlgorithmException nsa) {
    -                Log.e(K9.LOG_TAG, "NoSuchAlgorithmException in getHttpClient: " + nsa);
    +                Log.e(LOG_TAG, "NoSuchAlgorithmException in getHttpClient: " + nsa);
                     throw new MessagingException("NoSuchAlgorithmException in getHttpClient: " + nsa);
                 } catch (KeyManagementException kme) {
    -                Log.e(K9.LOG_TAG, "KeyManagementException in getHttpClient: " + kme);
    +                Log.e(LOG_TAG, "KeyManagementException in getHttpClient: " + kme);
                     throw new MessagingException("KeyManagementException in getHttpClient: " + kme);
                 }
             }
    @@ -1092,10 +1093,10 @@ public class WebDavStore extends RemoteStore {
                     istream = WebDavHttpClient.getUngzippedContent(entity);
                 }
             } catch (UnsupportedEncodingException uee) {
    -            Log.e(K9.LOG_TAG, "UnsupportedEncodingException: " + uee + "\nTrace: " + processException(uee));
    +            Log.e(LOG_TAG, "UnsupportedEncodingException: " + uee + "\nTrace: " + processException(uee));
                 throw new MessagingException("UnsupportedEncodingException", uee);
             } catch (IOException ioe) {
    -            Log.e(K9.LOG_TAG, "IOException: " + ioe + "\nTrace: " + processException(ioe));
    +            Log.e(LOG_TAG, "IOException: " + ioe + "\nTrace: " + processException(ioe));
                 throw new MessagingException("IOException", ioe);
             }
     
    @@ -1120,8 +1121,8 @@ public class WebDavStore extends RemoteStore {
                                        boolean needsParsing)
         throws MessagingException {
             DataSet dataset = new DataSet();
    -        if (K9.DEBUG && K9.DEBUG_PROTOCOL_WEBDAV) {
    -            Log.v(K9.LOG_TAG, "processRequest url = '" + url + "', method = '" + method + "', messageBody = '"
    +        if (K9MailLib.isDebug() && DEBUG_PROTOCOL_WEBDAV) {
    +            Log.v(LOG_TAG, "processRequest url = '" + url + "', method = '" + method + "', messageBody = '"
                       + messageBody + "'");
             }
     
    @@ -1153,10 +1154,10 @@ public class WebDavStore extends RemoteStore {
     
                         dataset = myHandler.getDataSet();
                     } catch (SAXException se) {
    -                    Log.e(K9.LOG_TAG, "SAXException in processRequest() " + se + "\nTrace: " + processException(se));
    +                    Log.e(LOG_TAG, "SAXException in processRequest() " + se + "\nTrace: " + processException(se));
                         throw new MessagingException("SAXException in processRequest() ", se);
                     } catch (ParserConfigurationException pce) {
    -                    Log.e(K9.LOG_TAG, "ParserConfigurationException in processRequest() " + pce + "\nTrace: "
    +                    Log.e(LOG_TAG, "ParserConfigurationException in processRequest() " + pce + "\nTrace: "
                               + processException(pce));
                         throw new MessagingException("ParserConfigurationException in processRequest() ", pce);
                     }
    @@ -1164,10 +1165,10 @@ public class WebDavStore extends RemoteStore {
                     istream.close();
                 }
             } catch (UnsupportedEncodingException uee) {
    -            Log.e(K9.LOG_TAG, "UnsupportedEncodingException: " + uee + "\nTrace: " + processException(uee));
    +            Log.e(LOG_TAG, "UnsupportedEncodingException: " + uee + "\nTrace: " + processException(uee));
                 throw new MessagingException("UnsupportedEncodingException in processRequest() ", uee);
             } catch (IOException ioe) {
    -            Log.e(K9.LOG_TAG, "IOException: " + ioe + "\nTrace: " + processException(ioe));
    +            Log.e(LOG_TAG, "IOException: " + ioe + "\nTrace: " + processException(ioe));
                 throw new MessagingException("IOException in processRequest() ", ioe);
             }
     
    @@ -1308,7 +1309,7 @@ public class WebDavStore extends RemoteStore {
                 headers.put("Brief", "t");
                 headers.put("If-Match", "*");
                 String action = (isMove ? "BMOVE" : "BCOPY");
    -            Log.i(K9.LOG_TAG, "Moving " + messages.size() + " messages to " + destFolder.mFolderUrl);
    +            Log.i(LOG_TAG, "Moving " + messages.size() + " messages to " + destFolder.mFolderUrl);
     
                 processRequest(mFolderUrl, action, messageBody, headers, false);
             }
    @@ -1331,8 +1332,8 @@ public class WebDavStore extends RemoteStore {
                 if (dataset != null) {
                     messageCount = dataset.getMessageCount();
                 }
    -            if (K9.DEBUG && K9.DEBUG_PROTOCOL_WEBDAV) {
    -                Log.v(K9.LOG_TAG, "Counted messages and webdav returned: "+messageCount);
    +            if (K9MailLib.isDebug() && DEBUG_PROTOCOL_WEBDAV) {
    +                Log.v(LOG_TAG, "Counted messages and webdav returned: "+messageCount);
                 }
     
                 return messageCount;
    @@ -1559,7 +1560,7 @@ public class WebDavStore extends RemoteStore {
                      */
                     if (wdMessage.getUrl().equals("")) {
                         wdMessage.setUrl(getMessageUrls(new String[] { wdMessage.getUid() }).get(wdMessage.getUid()));
    -                    Log.i(K9.LOG_TAG, "Fetching messages with UID = '" + wdMessage.getUid() + "', URL = '"
    +                    Log.i(LOG_TAG, "Fetching messages with UID = '" + wdMessage.getUid() + "', URL = '"
                               + wdMessage.getUrl() + "'");
                         if (wdMessage.getUrl().equals("")) {
                             throw new MessagingException("Unable to get URL for message");
    @@ -1567,7 +1568,7 @@ public class WebDavStore extends RemoteStore {
                     }
     
                     try {
    -                    Log.i(K9.LOG_TAG, "Fetching message with UID = '" + wdMessage.getUid() + "', URL = '"
    +                    Log.i(LOG_TAG, "Fetching message with UID = '" + wdMessage.getUid() + "', URL = '"
                               + wdMessage.getUrl() + "'");
                         HttpGet httpget = new HttpGet(new URI(wdMessage.getUrl()));
                         HttpResponse response;
    @@ -1623,13 +1624,13 @@ public class WebDavStore extends RemoteStore {
                         }
     
                     } catch (IllegalArgumentException iae) {
    -                    Log.e(K9.LOG_TAG, "IllegalArgumentException caught " + iae + "\nTrace: " + processException(iae));
    +                    Log.e(LOG_TAG, "IllegalArgumentException caught " + iae + "\nTrace: " + processException(iae));
                         throw new MessagingException("IllegalArgumentException caught", iae);
                     } catch (URISyntaxException use) {
    -                    Log.e(K9.LOG_TAG, "URISyntaxException caught " + use + "\nTrace: " + processException(use));
    +                    Log.e(LOG_TAG, "URISyntaxException caught " + use + "\nTrace: " + processException(use));
                         throw new MessagingException("URISyntaxException caught", use);
                     } catch (IOException ioe) {
    -                    Log.e(K9.LOG_TAG, "Non-success response code loading message, response code was " + statusCode
    +                    Log.e(LOG_TAG, "Non-success response code loading message, response code was " + statusCode
                               + "\nURL: " + wdMessage.getUrl() + "\nError: " + ioe.getMessage() + "\nTrace: "
                               + processException(ioe));
                         throw new MessagingException("Failure code " + statusCode, ioe);
    @@ -1700,7 +1701,7 @@ public class WebDavStore extends RemoteStore {
                     try {
                         wdMessage.setFlagInternal(Flag.SEEN, uidToReadStatus.get(wdMessage.getUid()));
                     } catch (NullPointerException e) {
    -                    Log.v(K9.LOG_TAG,"Under some weird circumstances, setting the read status when syncing from webdav threw an NPE. Skipping.");
    +                    Log.v(LOG_TAG,"Under some weird circumstances, setting the read status when syncing from webdav threw an NPE. Skipping.");
                     }
     
                     if (listener != null) {
    @@ -1769,7 +1770,7 @@ public class WebDavStore extends RemoteStore {
                         wdMessage.setNewHeaders(envelope);
                         wdMessage.setFlagInternal(Flag.SEEN, envelope.getReadStatus());
                     } else {
    -                    Log.e(K9.LOG_TAG,"Asked to get metadata for a non-existent message: "+wdMessage.getUid());
    +                    Log.e(LOG_TAG,"Asked to get metadata for a non-existent message: "+wdMessage.getUid());
                     }
     
                     if (listener != null) {
    @@ -1881,7 +1882,7 @@ public class WebDavStore extends RemoteStore {
                         }
                         messageURL += encodeUtf8(message.getUid() + ":" + System.currentTimeMillis() + ".eml");
     
    -                    Log.i(K9.LOG_TAG, "Uploading message as " + messageURL);
    +                    Log.i(LOG_TAG, "Uploading message as " + messageURL);
     
                         httpmethod = new HttpGeneric(messageURL);
                         httpmethod.setMethod("PUT");
    @@ -1920,7 +1921,7 @@ public class WebDavStore extends RemoteStore {
     
             @Override
             public String getUidFromMessageId(Message message) throws MessagingException {
    -            Log.e(K9.LOG_TAG,
    +            Log.e(LOG_TAG,
                       "Unimplemented method getUidFromMessageId in WebDavStore.WebDavFolder could lead to duplicate messages "
                       + " being uploaded to the Sent folder");
                 return null;
    @@ -1928,7 +1929,7 @@ public class WebDavStore extends RemoteStore {
     
             @Override
             public void setFlags(final Set flags, boolean value) throws MessagingException {
    -            Log.e(K9.LOG_TAG,
    +            Log.e(LOG_TAG,
                       "Unimplemented method setFlags(Set, boolean) breaks markAllMessagesAsRead and EmptyTrash");
                 // Try to make this efficient by not retrieving all of the messages
             }
    @@ -1970,7 +1971,7 @@ public class WebDavStore extends RemoteStore {
                     end = encodeUtf8(end);
                     end = end.replaceAll("\\+", "%20");
                 } catch (IllegalArgumentException iae) {
    -                Log.e(K9.LOG_TAG, "IllegalArgumentException caught in setUrl: " + iae + "\nTrace: "
    +                Log.e(LOG_TAG, "IllegalArgumentException caught in setUrl: " + iae + "\nTrace: "
                           + processException(iae));
                 }
     
    @@ -2020,7 +2021,7 @@ public class WebDavStore extends RemoteStore {
             @Override
             public void delete(String trashFolderName) throws MessagingException {
                 WebDavFolder wdFolder = (WebDavFolder) getFolder();
    -            Log.i(K9.LOG_TAG, "Deleting message by moving to " + trashFolderName);
    +            Log.i(LOG_TAG, "Deleting message by moving to " + trashFolderName);
                 wdFolder.moveMessages(Collections.singletonList(this), wdFolder.getStore().getFolder(trashFolderName));
             }
     
    @@ -2318,7 +2319,7 @@ public class WebDavStore extends RemoteStore {
                                     Date parsedDate = dfInput.parse(date);
                                     tempDate = dfOutput.format(parsedDate);
                                 } catch (java.text.ParseException pe) {
    -                                Log.e(K9.LOG_TAG, "Error parsing date: " + pe + "\nTrace: " + processException(pe));
    +                                Log.e(LOG_TAG, "Error parsing date: " + pe + "\nTrace: " + processException(pe));
                                 }
                                 envelope.addHeader(header, tempDate);
                             } else {
    @@ -2357,8 +2358,8 @@ public class WebDavStore extends RemoteStore {
             public HttpGeneric(final String uri) {
                 super();
     
    -            if (K9.DEBUG) {
    -                Log.v(K9.LOG_TAG, "Starting uri = '" + uri + "'");
    +            if (K9MailLib.isDebug()) {
    +                Log.v(LOG_TAG, "Starting uri = '" + uri + "'");
                 }
     
                 String[] urlParts = uri.split("/");
    @@ -2376,7 +2377,7 @@ public class WebDavStore extends RemoteStore {
                         end = end.replaceAll("\\+", "%20");
                     }
                 } catch (IllegalArgumentException iae) {
    -                Log.e(K9.LOG_TAG, "IllegalArgumentException caught in HttpGeneric(String uri): " + iae + "\nTrace: "
    +                Log.e(LOG_TAG, "IllegalArgumentException caught in HttpGeneric(String uri): " + iae + "\nTrace: "
                           + processException(iae));
                 }
     
    @@ -2387,13 +2388,13 @@ public class WebDavStore extends RemoteStore {
                         url = urlParts[i];
                     }
                 }
    -            if (K9.DEBUG && K9.DEBUG_PROTOCOL_WEBDAV) {
    -                Log.v(K9.LOG_TAG, "url = '" + url + "' length = " + url.length()
    +            if (K9MailLib.isDebug() && DEBUG_PROTOCOL_WEBDAV) {
    +                Log.v(LOG_TAG, "url = '" + url + "' length = " + url.length()
                           + ", end = '" + end + "' length = " + end.length());
                 }
                 url = url + "/" + end;
     
    -            Log.i(K9.LOG_TAG, "url = " + url);
    +            Log.i(LOG_TAG, "url = " + url);
                 setURI(URI.create(url));
             }
     
    @@ -2423,7 +2424,7 @@ public class WebDavStore extends RemoteStore {
              * the License for the specific language governing permissions and limitations under the License.
              */
             public static void modifyRequestToAcceptGzipResponse(HttpRequest request) {
    -            Log.i(K9.LOG_TAG, "Requesting gzipped data");
    +            Log.i(LOG_TAG, "Requesting gzipped data");
                 request.addHeader("Accept-Encoding", "gzip");
             }
     
    @@ -2439,7 +2440,7 @@ public class WebDavStore extends RemoteStore {
                 if (contentEncoding == null)
                     return responseStream;
                 if (contentEncoding.contains("gzip")) {
    -                Log.i(K9.LOG_TAG, "Response is gzipped");
    +                Log.i(LOG_TAG, "Response is gzipped");
                     responseStream = new GZIPInputStream(responseStream);
                 }
                 return responseStream;
    diff --git a/src/com/fsck/k9/mail/transport/SmtpTransport.java b/src/com/fsck/k9/mail/transport/SmtpTransport.java
    index 29fae5f1c..485e45f02 100644
    --- a/src/com/fsck/k9/mail/transport/SmtpTransport.java
    +++ b/src/com/fsck/k9/mail/transport/SmtpTransport.java
    @@ -26,6 +26,9 @@ import java.net.*;
     import java.security.GeneralSecurityException;
     import java.util.*;
     
    +import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_SMTP;
    +import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
    +
     public class SmtpTransport extends Transport {
         public static final String TRANSPORT_TYPE = "SMTP";
     
    @@ -303,8 +306,8 @@ public class SmtpTransport extends Transport {
                     try {
                         mLargestAcceptableMessage = Integer.parseInt(extensions.get("SIZE"));
                     } catch (Exception e) {
    -                    if (K9.DEBUG && K9.DEBUG_PROTOCOL_SMTP) {
    -                        Log.d(K9.LOG_TAG, "Tried to parse " + extensions.get("SIZE") + " and get an int", e);
    +                    if (K9MailLib.isDebug() && DEBUG_PROTOCOL_SMTP) {
    +                        Log.d(LOG_TAG, "Tried to parse " + extensions.get("SIZE") + " and get an int", e);
                         }
                     }
                 }
    @@ -436,14 +439,14 @@ public class SmtpTransport extends Transport {
                     extensions.put(pair[0].toUpperCase(Locale.US), pair.length == 1 ? "" : pair[1]);
                 }
             } catch (NegativeSmtpReplyException e) {
    -            if (K9.DEBUG) {
    -                Log.v(K9.LOG_TAG, "Server doesn't support the EHLO command. Trying HELO...");
    +            if (K9MailLib.isDebug()) {
    +                Log.v(LOG_TAG, "Server doesn't support the EHLO command. Trying HELO...");
                 }
     
                 try {
                     executeSimpleCommand("HELO " + host);
                 } catch (NegativeSmtpReplyException e2) {
    -                Log.w(K9.LOG_TAG, "Server doesn't support the HELO command. Continuing anyway.");
    +                Log.w(LOG_TAG, "Server doesn't support the HELO command. Continuing anyway.");
                 }
             }
             return extensions;
    @@ -527,7 +530,7 @@ public class SmtpTransport extends Transport {
                 // "5xx text" -responses are permanent failures
                 String msg = e.getMessage();
                 if (msg != null && msg.startsWith("5")) {
    -                Log.w(K9.LOG_TAG, "handling 5xx SMTP error code as a permanent failure");
    +                Log.w(LOG_TAG, "handling 5xx SMTP error code as a permanent failure");
                     possibleSend = false;
                 }
     
    @@ -580,21 +583,21 @@ public class SmtpTransport extends Transport {
                 }
             }
             String ret = sb.toString();
    -        if (K9.DEBUG && K9.DEBUG_PROTOCOL_SMTP)
    -            Log.d(K9.LOG_TAG, "SMTP <<< " + ret);
    +        if (K9MailLib.isDebug() && DEBUG_PROTOCOL_SMTP)
    +            Log.d(LOG_TAG, "SMTP <<< " + ret);
     
             return ret;
         }
     
         private void writeLine(String s, boolean sensitive) throws IOException {
    -        if (K9.DEBUG && K9.DEBUG_PROTOCOL_SMTP) {
    +        if (K9MailLib.isDebug() && DEBUG_PROTOCOL_SMTP) {
                 final String commandToLog;
    -            if (sensitive && !K9.DEBUG_SENSITIVE) {
    +            if (sensitive && !K9MailLib.isDebugSensitive()) {
                     commandToLog = "SMTP >>> *sensitive*";
                 } else {
                     commandToLog = "SMTP >>> " + s;
                 }
    -            Log.d(K9.LOG_TAG, commandToLog);
    +            Log.d(LOG_TAG, commandToLog);
             }
     
             byte[] data = s.concat("\r\n").getBytes();
    diff --git a/src/com/fsck/k9/mail/transport/WebDavTransport.java b/src/com/fsck/k9/mail/transport/WebDavTransport.java
    index 839ec00d9..2693aa8c4 100644
    --- a/src/com/fsck/k9/mail/transport/WebDavTransport.java
    +++ b/src/com/fsck/k9/mail/transport/WebDavTransport.java
    @@ -3,7 +3,7 @@ package com.fsck.k9.mail.transport;
     
     import android.util.Log;
     
    -import com.fsck.k9.K9;
    +import com.fsck.k9.mail.K9MailLib;
     import com.fsck.k9.mail.Message;
     import com.fsck.k9.mail.MessagingException;
     import com.fsck.k9.mail.ServerSettings;
    @@ -13,6 +13,8 @@ import com.fsck.k9.mail.store.WebDavStore;
     
     import java.util.Collections;
     
    +import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
    +
     public class WebDavTransport extends Transport {
         public static final String TRANSPORT_TYPE = WebDavStore.STORE_TYPE;
     
    @@ -46,14 +48,14 @@ public class WebDavTransport extends Transport {
         public WebDavTransport(StoreConfig storeConfig) throws MessagingException {
             store = new WebDavStore(storeConfig);
     
    -        if (K9.DEBUG)
    -            Log.d(K9.LOG_TAG, ">>> New WebDavTransport creation complete");
    +        if (K9MailLib.isDebug())
    +            Log.d(LOG_TAG, ">>> New WebDavTransport creation complete");
         }
     
         @Override
         public void open() throws MessagingException {
    -        if (K9.DEBUG)
    -            Log.d(K9.LOG_TAG, ">>> open called on WebDavTransport ");
    +        if (K9MailLib.isDebug())
    +            Log.d(LOG_TAG, ">>> open called on WebDavTransport ");
     
             store.getHttpClient();
         }