mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-23 18:02:15 -05:00
Merge pull request #516 from k9mail/untangle-network-code
Untangle network code
This commit is contained in:
commit
deb11b2226
@ -29,9 +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.store.StorageManager;
|
||||
import com.fsck.k9.mail.store.StorageManager.StorageProvider;
|
||||
import com.fsck.k9.mail.store.local.LocalStore;
|
||||
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.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;
|
||||
@ -40,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;
|
||||
|
||||
@ -48,7 +51,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)
|
||||
*/
|
||||
@ -361,9 +364,9 @@ public class Account implements BaseAccount {
|
||||
|
||||
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);
|
||||
@ -689,9 +692,9 @@ public class Account implements BaseAccount {
|
||||
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);
|
||||
@ -1076,8 +1079,8 @@ public class Account implements BaseAccount {
|
||||
return mDraftsFolderName;
|
||||
}
|
||||
|
||||
public synchronized void setDraftsFolderName(String draftsFolderName) {
|
||||
mDraftsFolderName = draftsFolderName;
|
||||
public synchronized void setDraftsFolderName(String name) {
|
||||
mDraftsFolderName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1096,8 +1099,8 @@ public class Account implements BaseAccount {
|
||||
return K9.ERROR_FOLDER_NAME;
|
||||
}
|
||||
|
||||
public synchronized void setSentFolderName(String sentFolderName) {
|
||||
mSentFolderName = sentFolderName;
|
||||
public synchronized void setSentFolderName(String name) {
|
||||
mSentFolderName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1113,8 +1116,8 @@ public class Account implements BaseAccount {
|
||||
return mTrashFolderName;
|
||||
}
|
||||
|
||||
public synchronized void setTrashFolderName(String trashFolderName) {
|
||||
mTrashFolderName = trashFolderName;
|
||||
public synchronized void setTrashFolderName(String name) {
|
||||
mTrashFolderName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1145,8 +1148,8 @@ public class Account implements BaseAccount {
|
||||
return mSpamFolderName;
|
||||
}
|
||||
|
||||
public synchronized void setSpamFolderName(String spamFolderName) {
|
||||
mSpamFolderName = spamFolderName;
|
||||
public synchronized void setSpamFolderName(String name) {
|
||||
mSpamFolderName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1165,8 +1168,8 @@ public class Account implements BaseAccount {
|
||||
return mAutoExpandFolderName;
|
||||
}
|
||||
|
||||
public synchronized void setAutoExpandFolderName(String autoExpandFolderName) {
|
||||
mAutoExpandFolderName = autoExpandFolderName;
|
||||
public synchronized void setAutoExpandFolderName(String name) {
|
||||
mAutoExpandFolderName = name;
|
||||
}
|
||||
|
||||
public synchronized int getAccountNumber() {
|
||||
@ -1289,11 +1292,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
|
||||
@ -1657,8 +1660,8 @@ public class Account implements BaseAccount {
|
||||
return mInboxFolderName;
|
||||
}
|
||||
|
||||
public void setInboxFolderName(String mInboxFolderName) {
|
||||
this.mInboxFolderName = mInboxFolderName;
|
||||
public void setInboxFolderName(String name) {
|
||||
this.mInboxFolderName = name;
|
||||
}
|
||||
|
||||
public synchronized boolean syncRemoteDeletions() {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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.mailstore.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;
|
||||
@ -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.
|
||||
|
@ -13,7 +13,8 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.mail.Store;
|
||||
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,7 +122,12 @@ public class Preferences {
|
||||
accountsInOrder.remove(account);
|
||||
}
|
||||
|
||||
Store.removeAccount(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();
|
||||
account.delete(this);
|
||||
|
@ -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,13 +75,12 @@ 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.StorageManager;
|
||||
import com.fsck.k9.mail.store.RemoteStore;
|
||||
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;
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -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.mailstore.LocalFolder;
|
||||
import com.fsck.k9.search.LocalSearch;
|
||||
import com.fsck.k9.search.SearchSpecification.Attribute;
|
||||
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
||||
@ -749,20 +748,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;
|
||||
|
@ -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;
|
||||
|
@ -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.helper.HtmlConverter;
|
||||
import com.fsck.k9.helper.IdentityHelper;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
@ -100,16 +101,17 @@ 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;
|
||||
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.TempFileBody;
|
||||
import com.fsck.k9.mail.store.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;
|
||||
@ -435,17 +437,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,
|
||||
LocalMessage 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 +468,22 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
*/
|
||||
public static void actionReply(
|
||||
Context context,
|
||||
Account account,
|
||||
Message message,
|
||||
LocalMessage 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,
|
||||
LocalMessage message,
|
||||
String messageBody) {
|
||||
Intent i = new Intent(context, MessageCompose.class);
|
||||
i.putExtra(EXTRA_MESSAGE_BODY, messageBody);
|
||||
i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference());
|
||||
@ -1339,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));
|
||||
@ -1658,7 +1655,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) {
|
||||
@ -1666,16 +1663,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()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2646,7 +2643,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 +2797,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 +2849,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());
|
||||
}
|
||||
|
||||
@ -2962,7 +2959,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
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 = MimeUtility.getTextFromPart(part);
|
||||
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() + ".");
|
||||
}
|
||||
@ -3027,7 +3024,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
boolean viewMessageContent) throws MessagingException {
|
||||
Part textPart = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
||||
if (textPart != null) {
|
||||
String text = MimeUtility.getTextFromPart(textPart);
|
||||
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() + ".");
|
||||
}
|
||||
@ -3230,7 +3227,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
if (K9.DEBUG) {
|
||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, HTML found.");
|
||||
}
|
||||
return MimeUtility.getTextFromPart(part);
|
||||
return MessageExtractor.getTextFromPart(part);
|
||||
}
|
||||
|
||||
part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
||||
@ -3238,7 +3235,8 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
if (K9.DEBUG) {
|
||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, text found.");
|
||||
}
|
||||
return HtmlConverter.textToHtml(MimeUtility.getTextFromPart(part));
|
||||
String text = MessageExtractor.getTextFromPart(part);
|
||||
return HtmlConverter.textToHtml(text);
|
||||
}
|
||||
} else if (format == SimpleMessageFormat.TEXT) {
|
||||
// Text takes precedence, then html.
|
||||
@ -3247,7 +3245,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
if (K9.DEBUG) {
|
||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, text found.");
|
||||
}
|
||||
return MimeUtility.getTextFromPart(part);
|
||||
return MessageExtractor.getTextFromPart(part);
|
||||
}
|
||||
|
||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||
@ -3255,7 +3253,8 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
if (K9.DEBUG) {
|
||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, HTML found.");
|
||||
}
|
||||
return HtmlConverter.htmlToText(MimeUtility.getTextFromPart(part));
|
||||
String text = MessageExtractor.getTextFromPart(part);
|
||||
return HtmlConverter.htmlToText(text);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3437,7 +3436,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
}
|
||||
updateMessageFormat();
|
||||
} else {
|
||||
processSourceMessage(message);
|
||||
processSourceMessage((LocalMessage) message);
|
||||
mSourceProcessed = true;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
package com.fsck.k9.activity;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fsck.k9.mail.Message;
|
||||
|
||||
import com.fsck.k9.mailstore.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;
|
||||
|
@ -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.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;
|
||||
@ -1193,23 +1193,23 @@ public class MessageList extends K9Activity implements MessageListFragmentListen
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResendMessage(Message message) {
|
||||
public void onResendMessage(LocalMessage message) {
|
||||
MessageCompose.actionEditDraft(this, message.makeMessageReference());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onForward(Message message) {
|
||||
MessageCompose.actionForward(this, message.getFolder().getAccount(), message, null);
|
||||
public void onForward(LocalMessage message) {
|
||||
MessageCompose.actionForward(this, message, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReply(Message message) {
|
||||
MessageCompose.actionReply(this, message.getFolder().getAccount(), message, false, null);
|
||||
public void onReply(LocalMessage message) {
|
||||
MessageCompose.actionReply(this, message, false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReplyAll(Message message) {
|
||||
MessageCompose.actionReply(this, message.getFolder().getAccount(), message, true, null);
|
||||
public void onReplyAll(LocalMessage message) {
|
||||
MessageCompose.actionReply(this, message, true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1399,18 +1399,18 @@ public class MessageList extends K9Activity implements MessageListFragmentListen
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReply(Message message, PgpData pgpData) {
|
||||
MessageCompose.actionReply(this, mAccount, message, false, pgpData.getDecryptedData());
|
||||
public void onReply(LocalMessage message, PgpData pgpData) {
|
||||
MessageCompose.actionReply(this, message, false, pgpData.getDecryptedData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReplyAll(Message message, PgpData pgpData) {
|
||||
MessageCompose.actionReply(this, mAccount, message, true, pgpData.getDecryptedData());
|
||||
public void onReplyAll(LocalMessage message, PgpData pgpData) {
|
||||
MessageCompose.actionReply(this, message, true, pgpData.getDecryptedData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onForward(Message mMessage, PgpData mPgpData) {
|
||||
MessageCompose.actionForward(this, mAccount, mMessage, mPgpData.getDecryptedData());
|
||||
public void onForward(LocalMessage mMessage, PgpData mPgpData) {
|
||||
MessageCompose.actionForward(this, mMessage, mPgpData.getDecryptedData());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -8,11 +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.mailstore.LocalFolder;
|
||||
import com.fsck.k9.mailstore.LocalMessage;
|
||||
import com.fsck.k9.mail.filter.Base64;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
@ -49,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();
|
||||
@ -80,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());
|
||||
@ -128,13 +128,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 {
|
||||
|
@ -1,14 +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.activity.InsertableHtmlContent;
|
||||
import com.fsck.k9.helper.HtmlConverter;
|
||||
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;
|
||||
@ -27,7 +27,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() {
|
@ -30,7 +30,7 @@ import android.widget.TextView;
|
||||
* <li>{@link #actionUpgradeDatabases(Context, Intent)} will call {@link K9#areDatabasesUpToDate()}
|
||||
* to check if we already know whether the databases have been upgraded.</li>
|
||||
* <li>{@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.mailstore.LocalStore#DB_VERSION}. This
|
||||
* is done as an optimization because it's faster than opening all of the accounts' databases
|
||||
* one by one.</li>
|
||||
* <li>If there was an error reading the cached database version or if it shows the databases need
|
||||
|
@ -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.mailstore.LocalFolder;
|
||||
import com.fsck.k9.mailstore.StorageManager;
|
||||
import com.fsck.k9.service.MailService;
|
||||
|
||||
import org.openintents.openpgp.util.OpenPgpListPreference;
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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.mailstore.LocalFolder;
|
||||
import com.fsck.k9.mailstore.LocalStore;
|
||||
import com.fsck.k9.service.MailService;
|
||||
|
||||
public class FolderSettings extends K9PreferenceActivity {
|
||||
|
14
src/com/fsck/k9/cache/EmailProviderCache.java
vendored
14
src/com/fsck/k9/cache/EmailProviderCache.java
vendored
@ -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.mailstore.LocalFolder;
|
||||
import com.fsck.k9.mailstore.LocalMessage;
|
||||
import com.fsck.k9.provider.EmailProvider;
|
||||
|
||||
/**
|
||||
@ -123,13 +123,11 @@ public class EmailProviderCache {
|
||||
}
|
||||
}
|
||||
|
||||
public void hideMessages(List<Message> messages) {
|
||||
public void hideMessages(List<LocalMessage> 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
@ -78,16 +79,18 @@ 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;
|
||||
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.mailstore.MessageRemovalListener;
|
||||
import com.fsck.k9.mail.MessageRetrievalListener;
|
||||
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.mail.store.UnavailableAccountException;
|
||||
import com.fsck.k9.mail.store.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;
|
||||
@ -212,7 +215,7 @@ public class MessagingController implements Runnable {
|
||||
* Don't modify this list directly, but use {@link addMessage} and
|
||||
* {@link removeMatchingMessage} instead.
|
||||
*/
|
||||
LinkedList<Message> messages;
|
||||
LinkedList<LocalMessage> 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
|
||||
@ -236,7 +239,7 @@ public class MessagingController implements Runnable {
|
||||
public NotificationData(int unread) {
|
||||
unreadBeforeNotification = unread;
|
||||
droppedMessages = new LinkedList<MessageReference>();
|
||||
messages = new LinkedList<Message>();
|
||||
messages = new LinkedList<LocalMessage>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -247,9 +250,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);
|
||||
@ -270,10 +273,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();
|
||||
@ -291,7 +294,7 @@ public class MessagingController implements Runnable {
|
||||
* List.
|
||||
*/
|
||||
public void supplyAllMessageRefs(List<MessageReference> refs) {
|
||||
for (Message m : messages) {
|
||||
for (LocalMessage m : messages) {
|
||||
refs.add(m.makeMessageReference());
|
||||
}
|
||||
refs.addAll(droppedMessages);
|
||||
@ -312,7 +315,7 @@ public class MessagingController implements Runnable {
|
||||
|
||||
private static final Set<Flag> SYNC_FLAGS = EnumSet.of(Flag.SEEN, Flag.FLAGGED, Flag.ANSWERED, Flag.FORWARDED);
|
||||
|
||||
private void suppressMessages(Account account, List<Message> messages) {
|
||||
private void suppressMessages(Account account, List<LocalMessage> messages) {
|
||||
EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(),
|
||||
mApplication.getApplicationContext());
|
||||
cache.hideMessages(messages);
|
||||
@ -324,13 +327,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().getAccountUuid(),
|
||||
mApplication.getApplicationContext());
|
||||
return cache.isMessageHidden(messageId, folderId);
|
||||
}
|
||||
@ -690,15 +691,16 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
|
||||
// Collecting statistics of the search result
|
||||
MessageRetrievalListener retrievalListener = new MessageRetrievalListener() {
|
||||
MessageRetrievalListener retrievalListener = new MessageRetrievalListener<LocalMessage>() {
|
||||
@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<Message> messages = new ArrayList<Message>();
|
||||
|
||||
public void messageFinished(LocalMessage message, int number, int ofTotal) {
|
||||
if (!isMessageSuppressed(message)) {
|
||||
List<LocalMessage> messages = new ArrayList<LocalMessage>();
|
||||
|
||||
messages.add(message);
|
||||
stats.unreadMessageCount += (!message.isSet(Flag.SEEN)) ? 1 : 0;
|
||||
@ -762,7 +764,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<Message> extraResults = new ArrayList<Message>();
|
||||
@ -791,7 +793,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 +813,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 +880,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 +1457,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchUnsyncedMessages(final Account account, final Folder remoteFolder,
|
||||
private <T extends Message> void fetchUnsyncedMessages(final Account account, final Folder<T> remoteFolder,
|
||||
final LocalFolder localFolder,
|
||||
List<Message> unsyncedMessages,
|
||||
final List<Message> smallMessages,
|
||||
@ -1473,9 +1475,9 @@ public class MessagingController implements Runnable {
|
||||
final List<Message> chunk = new ArrayList<Message>(UNSYNC_CHUNK_SIZE);
|
||||
|
||||
remoteFolder.fetch(unsyncedMessages, fp,
|
||||
new MessageRetrievalListener() {
|
||||
new MessageRetrievalListener<T>() {
|
||||
@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 +1566,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 +1594,9 @@ public class MessagingController implements Runnable {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void downloadSmallMessages(final Account account, final Folder remoteFolder,
|
||||
private <T extends Message> void downloadSmallMessages(final Account account, final Folder<T> remoteFolder,
|
||||
final LocalFolder localFolder,
|
||||
List<Message> smallMessages,
|
||||
List<T> smallMessages,
|
||||
final AtomicInteger progress,
|
||||
final int unreadBeforeStart,
|
||||
final AtomicInteger newMessages,
|
||||
@ -1608,9 +1610,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<T>() {
|
||||
@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)) {
|
||||
@ -1620,7 +1622,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();
|
||||
@ -1671,9 +1673,9 @@ public class MessagingController implements Runnable {
|
||||
|
||||
|
||||
|
||||
private void downloadLargeMessages(final Account account, final Folder remoteFolder,
|
||||
private <T extends Message> void downloadLargeMessages(final Account account, final Folder<T> remoteFolder,
|
||||
final LocalFolder localFolder,
|
||||
List<Message> largeMessages,
|
||||
List<T> largeMessages,
|
||||
final AtomicInteger progress,
|
||||
final int unreadBeforeStart,
|
||||
final AtomicInteger newMessages,
|
||||
@ -1744,7 +1746,7 @@ public class MessagingController implements Runnable {
|
||||
* right now, attachments will be left for later.
|
||||
*/
|
||||
|
||||
Set<Part> viewables = MimeUtility.collectTextParts(message);
|
||||
Set<Part> viewables = MessageExtractor.collectTextParts(message);
|
||||
|
||||
/*
|
||||
* Now download the parts we're interested in storing.
|
||||
@ -1768,7 +1770,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.
|
||||
@ -1821,11 +1823,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 +1861,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;
|
||||
}
|
||||
@ -2722,7 +2724,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));
|
||||
@ -2857,7 +2859,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<Message> messages, Flag flag,
|
||||
public void setFlag(Account account, String folderName, List<? extends Message> 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.
|
||||
@ -3197,7 +3199,7 @@ public class MessagingController implements Runnable {
|
||||
try {
|
||||
LocalStore localStore = account.getLocalStore();
|
||||
|
||||
List<Part> attachments = MimeUtility.collectAttachments(message);
|
||||
List<Part> attachments = MessageExtractor.collectAttachments(message);
|
||||
for (Part attachment : attachments) {
|
||||
attachment.setBody(null);
|
||||
}
|
||||
@ -3775,7 +3777,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
}
|
||||
public void moveMessages(final Account account, final String srcFolder,
|
||||
final List<Message> messages, final String destFolder,
|
||||
final List<LocalMessage> messages, final String destFolder,
|
||||
final MessagingListener listener) {
|
||||
|
||||
suppressMessages(account, messages);
|
||||
@ -3790,7 +3792,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
|
||||
public void moveMessagesInThread(final Account account, final String srcFolder,
|
||||
final List<Message> messages, final String destFolder) {
|
||||
final List<LocalMessage> messages, final String destFolder) {
|
||||
|
||||
suppressMessages(account, messages);
|
||||
|
||||
@ -3808,14 +3810,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<Message> messages, final String destFolder,
|
||||
final List<? extends Message> messages, final String destFolder,
|
||||
final MessagingListener listener) {
|
||||
|
||||
putBackground("copyMessages", null, new Runnable() {
|
||||
@ -3828,7 +3830,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
|
||||
public void copyMessagesInThread(final Account account, final String srcFolder,
|
||||
final List<Message> messages, final String destFolder) {
|
||||
final List<? extends Message> messages, final String destFolder) {
|
||||
|
||||
putBackground("copyMessagesInThread", null, new Runnable() {
|
||||
@Override
|
||||
@ -3851,7 +3853,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
|
||||
private void moveOrCopyMessageSynchronous(final Account account, final String srcFolder,
|
||||
final List<Message> inMessages, final String destFolder, final boolean isCopy,
|
||||
final List<? extends Message> inMessages, final String destFolder, final boolean isCopy,
|
||||
MessagingListener listener) {
|
||||
|
||||
try {
|
||||
@ -3962,7 +3964,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 +3976,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteThreads(final List<Message> messages) {
|
||||
public void deleteThreads(final List<LocalMessage> messages) {
|
||||
actOnMessages(messages, new MessageActor() {
|
||||
|
||||
@Override
|
||||
@ -4006,7 +4008,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public List<Message> collectMessagesInThreads(Account account, List<Message> messages)
|
||||
public List<Message> collectMessagesInThreads(Account account, List<? extends Message> messages)
|
||||
throws MessagingException {
|
||||
|
||||
LocalStore localStore = account.getLocalStore();
|
||||
@ -4025,7 +4027,7 @@ public class MessagingController implements Runnable {
|
||||
return messagesInThreads;
|
||||
}
|
||||
|
||||
public void deleteMessages(final List<Message> messages, final MessagingListener listener) {
|
||||
public void deleteMessages(final List<LocalMessage> messages, final MessagingListener listener) {
|
||||
actOnMessages(messages, new MessageActor() {
|
||||
|
||||
@Override
|
||||
@ -4244,13 +4246,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 = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
||||
if (part == null) {
|
||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||
}
|
||||
if (part != null) {
|
||||
quotedText = MimeUtility.getTextFromPart(part);
|
||||
quotedText = MessageExtractor.getTextFromPart(part);
|
||||
}
|
||||
if (quotedText != null) {
|
||||
msg.putExtra(Intent.EXTRA_TEXT, quotedText);
|
||||
@ -4393,7 +4394,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();
|
||||
@ -4696,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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4706,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);
|
||||
@ -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<Message> messages) {
|
||||
List<? extends Message> messages) {
|
||||
if (!platformSupportsLockScreenNotifications()) {
|
||||
return;
|
||||
}
|
||||
@ -5307,7 +5306,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();
|
||||
@ -5685,15 +5683,15 @@ public class MessagingController implements Runnable {
|
||||
|
||||
}
|
||||
|
||||
private void actOnMessages(List<Message> messages, MessageActor actor) {
|
||||
private void actOnMessages(List<LocalMessage> messages, MessageActor actor) {
|
||||
Map<Account, Map<Folder, List<Message>>> accountMap = new HashMap<Account, Map<Folder, List<Message>>>();
|
||||
|
||||
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<Folder, List<Message>> folderMap = accountMap.get(account);
|
||||
if (folderMap == null) {
|
||||
|
@ -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.mailstore.LocalFolder;
|
||||
import com.fsck.k9.mailstore.LocalStore;
|
||||
import com.fsck.k9.service.SleepService;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -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.mailstore.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<Message> messages) {}
|
||||
List<LocalMessage> 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<Message> extraResults) {}
|
||||
public void remoteSearchFinished(String folder, int numResults, int maxResults, List<Message> 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
|
||||
|
@ -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)}.<br/>
|
||||
* An {@link com.fsck.k9.Account} is not
|
||||
* {@link com.fsck.k9.Account#isAvailable(android.content.Context)}.<br/>
|
||||
* The operation may be retried later.
|
||||
*/
|
||||
public class UnavailableAccountException extends RuntimeException {
|
@ -7,8 +7,10 @@ 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 {
|
||||
|
||||
public static Pattern PGP_MESSAGE =
|
||||
@ -37,7 +39,7 @@ public class CryptoHelper {
|
||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||
}
|
||||
if (part != null) {
|
||||
data = MimeUtility.getTextFromPart(part);
|
||||
data = MessageExtractor.getTextFromPart(part);
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
// guess not...
|
||||
@ -60,7 +62,7 @@ public class CryptoHelper {
|
||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||
}
|
||||
if (part != null) {
|
||||
data = MimeUtility.getTextFromPart(part);
|
||||
data = MessageExtractor.getTextFromPart(part);
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
// guess not...
|
||||
|
@ -88,8 +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.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;
|
||||
@ -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<Message> mActiveMessages;
|
||||
private List<LocalMessage> 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);
|
||||
@ -1189,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);
|
||||
}
|
||||
|
||||
@ -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<Message> messages) {
|
||||
private void onDelete(List<LocalMessage> 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<Message> messages) {
|
||||
private void onDeleteConfirmed(List<LocalMessage> 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<Message> messages = mActiveMessages;
|
||||
final List<LocalMessage> 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) {
|
||||
@ -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;
|
||||
}
|
||||
@ -1539,7 +1535,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 +1558,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 +1703,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 +1717,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 +1728,12 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remoteSearchFinished(Account acct, String folder, int numResults, List<Message> extraResults) {
|
||||
public void remoteSearchFinished(String folder, int numResults, int maxResults, List<Message> 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 +1742,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 +2408,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 +2418,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
* @param messages
|
||||
* Never {@code null}.
|
||||
*/
|
||||
private void onMove(List<Message> messages) {
|
||||
private void onMove(List<LocalMessage> messages) {
|
||||
if (!checkCopyOrMovePossible(messages, FolderOperation.MOVE)) {
|
||||
return;
|
||||
}
|
||||
@ -2440,12 +2432,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().getAccountUuid(), null,
|
||||
messages);
|
||||
}
|
||||
|
||||
private void onCopy(Message message) {
|
||||
private void onCopy(LocalMessage message) {
|
||||
onCopy(Collections.singletonList(message));
|
||||
}
|
||||
|
||||
@ -2455,7 +2448,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
* @param messages
|
||||
* Never {@code null}.
|
||||
*/
|
||||
private void onCopy(List<Message> messages) {
|
||||
private void onCopy(List<LocalMessage> messages) {
|
||||
if (!checkCopyOrMovePossible(messages, FolderOperation.COPY)) {
|
||||
return;
|
||||
}
|
||||
@ -2469,7 +2462,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().getAccountUuid(),
|
||||
null,
|
||||
messages);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2486,12 +2482,13 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
*
|
||||
* @see #startActivityForResult(Intent, int)
|
||||
*/
|
||||
private void displayFolderChoice(int requestCode, Account account, Folder folder,
|
||||
List<Message> messages) {
|
||||
private void displayFolderChoice(int requestCode, Folder folder,
|
||||
String accountUuid, String lastSelectedFolderName,
|
||||
List<LocalMessage> 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, accountUuid);
|
||||
intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, lastSelectedFolderName);
|
||||
|
||||
if (folder == null) {
|
||||
intent.putExtra(ChooseFolder.EXTRA_SHOW_CURRENT, "yes");
|
||||
@ -2504,14 +2501,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<Message> messages) {
|
||||
Map<Account, List<Message>> messagesByAccount = groupMessagesByAccount(messages);
|
||||
private void onArchive(final List<LocalMessage> messages) {
|
||||
Map<Account, List<LocalMessage>> messagesByAccount = groupMessagesByAccount(messages);
|
||||
|
||||
for (Entry<Account, List<Message>> entry : messagesByAccount.entrySet()) {
|
||||
for (Entry<Account, List<LocalMessage>> entry : messagesByAccount.entrySet()) {
|
||||
Account account = entry.getKey();
|
||||
String archiveFolder = account.getArchiveFolderName();
|
||||
|
||||
@ -2521,14 +2518,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Account, List<Message>> groupMessagesByAccount(final List<Message> messages) {
|
||||
Map<Account, List<Message>> messagesByAccount = new HashMap<Account, List<Message>>();
|
||||
for (Message message : messages) {
|
||||
Account account = message.getFolder().getAccount();
|
||||
private Map<Account, List<LocalMessage>> groupMessagesByAccount(final List<LocalMessage> messages) {
|
||||
Map<Account, List<LocalMessage>> messagesByAccount = new HashMap<Account, List<LocalMessage>>();
|
||||
for (LocalMessage message : messages) {
|
||||
Account account = message.getAccount();
|
||||
|
||||
List<Message> msgList = messagesByAccount.get(account);
|
||||
List<LocalMessage> msgList = messagesByAccount.get(account);
|
||||
if (msgList == null) {
|
||||
msgList = new ArrayList<Message>();
|
||||
msgList = new ArrayList<LocalMessage>();
|
||||
messagesByAccount.put(account, msgList);
|
||||
}
|
||||
|
||||
@ -2537,7 +2534,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 +2544,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<Message> messages) {
|
||||
private void onSpam(List<LocalMessage> messages) {
|
||||
if (K9.confirmSpam()) {
|
||||
// remember the message selection for #onCreateDialog(int)
|
||||
mActiveMessages = messages;
|
||||
@ -2557,10 +2554,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
}
|
||||
}
|
||||
|
||||
private void onSpamConfirmed(List<Message> messages) {
|
||||
Map<Account, List<Message>> messagesByAccount = groupMessagesByAccount(messages);
|
||||
private void onSpamConfirmed(List<LocalMessage> messages) {
|
||||
Map<Account, List<LocalMessage>> messagesByAccount = groupMessagesByAccount(messages);
|
||||
|
||||
for (Entry<Account, List<Message>> entry : messagesByAccount.entrySet()) {
|
||||
for (Entry<Account, List<LocalMessage>> entry : messagesByAccount.entrySet()) {
|
||||
Account account = entry.getKey();
|
||||
String spamFolder = account.getSpamFolderName();
|
||||
|
||||
@ -2584,7 +2581,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
*
|
||||
* @return {@code true}, if operation is possible.
|
||||
*/
|
||||
private boolean checkCopyOrMovePossible(final List<Message> messages,
|
||||
private boolean checkCopyOrMovePossible(final List<LocalMessage> messages,
|
||||
final FolderOperation operation) {
|
||||
|
||||
if (messages.isEmpty()) {
|
||||
@ -2592,11 +2589,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 +2619,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
* @param destination
|
||||
* The name of the destination folder. Never {@code null}.
|
||||
*/
|
||||
private void copy(List<Message> messages, final String destination) {
|
||||
private void copy(List<LocalMessage> messages, final String destination) {
|
||||
copyOrMove(messages, destination, FolderOperation.COPY);
|
||||
}
|
||||
|
||||
@ -2634,7 +2631,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
* @param destination
|
||||
* The name of the destination folder. Never {@code null}.
|
||||
*/
|
||||
private void move(List<Message> messages, final String destination) {
|
||||
private void move(List<LocalMessage> messages, final String destination) {
|
||||
copyOrMove(messages, destination, FolderOperation.MOVE);
|
||||
}
|
||||
|
||||
@ -2650,12 +2647,12 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
* @param operation
|
||||
* Specifies what operation to perform. Never {@code null}.
|
||||
*/
|
||||
private void copyOrMove(List<Message> messages, final String destination,
|
||||
private void copyOrMove(List<LocalMessage> messages, final String destination,
|
||||
final FolderOperation operation) {
|
||||
|
||||
Map<String, List<Message>> folderMap = new HashMap<String, List<Message>>();
|
||||
Map<String, List<LocalMessage>> folderMap = new HashMap<String, List<LocalMessage>>();
|
||||
|
||||
for (Message message : messages) {
|
||||
for (LocalMessage message : messages) {
|
||||
if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(message)) ||
|
||||
(operation == FolderOperation.COPY && !mController.isCopyCapable(message))) {
|
||||
|
||||
@ -2674,19 +2671,19 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Message> outMessages = folderMap.get(folderName);
|
||||
List<LocalMessage> outMessages = folderMap.get(folderName);
|
||||
if (outMessages == null) {
|
||||
outMessages = new ArrayList<Message>();
|
||||
outMessages = new ArrayList<LocalMessage>();
|
||||
folderMap.put(folderName, outMessages);
|
||||
}
|
||||
|
||||
outMessages.add(message);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, List<Message>> entry : folderMap.entrySet()) {
|
||||
for (Map.Entry<String, List<LocalMessage>> entry : folderMap.entrySet()) {
|
||||
String folderName = entry.getKey();
|
||||
List<Message> outMessages = entry.getValue();
|
||||
Account account = outMessages.get(0).getFolder().getAccount();
|
||||
List<LocalMessage> outMessages = entry.getValue();
|
||||
Account account = outMessages.get(0).getAccount();
|
||||
|
||||
if (operation == FolderOperation.MOVE) {
|
||||
if (mThreadedList) {
|
||||
@ -2859,7 +2856,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
*/
|
||||
switch (item.getItemId()) {
|
||||
case R.id.delete: {
|
||||
List<Message> messages = getCheckedMessages();
|
||||
List<LocalMessage> messages = getCheckedMessages();
|
||||
onDelete(messages);
|
||||
mSelectedCount = 0;
|
||||
break;
|
||||
@ -2887,26 +2884,22 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
|
||||
// only if the account supports this
|
||||
case R.id.archive: {
|
||||
List<Message> messages = getCheckedMessages();
|
||||
onArchive(messages);
|
||||
onArchive(getCheckedMessages());
|
||||
mSelectedCount = 0;
|
||||
break;
|
||||
}
|
||||
case R.id.spam: {
|
||||
List<Message> messages = getCheckedMessages();
|
||||
onSpam(messages);
|
||||
onSpam(getCheckedMessages());
|
||||
mSelectedCount = 0;
|
||||
break;
|
||||
}
|
||||
case R.id.move: {
|
||||
List<Message> messages = getCheckedMessages();
|
||||
onMove(messages);
|
||||
onMove(getCheckedMessages());
|
||||
mSelectedCount = 0;
|
||||
break;
|
||||
}
|
||||
case R.id.copy: {
|
||||
List<Message> messages = getCheckedMessages();
|
||||
onCopy(messages);
|
||||
onCopy(getCheckedMessages());
|
||||
mSelectedCount = 0;
|
||||
break;
|
||||
}
|
||||
@ -2987,7 +2980,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);
|
||||
@ -3116,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);
|
||||
@ -3135,7 +3128,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 +3151,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 +3161,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 +3172,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Message> getCheckedMessages() {
|
||||
List<Message> messages = new ArrayList<Message>(mSelected.size());
|
||||
private List<LocalMessage> getCheckedMessages() {
|
||||
List<LocalMessage> messages = new ArrayList<LocalMessage>(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 +3190,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 +3220,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);
|
||||
}
|
||||
|
@ -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.mailstore.LocalMessage;
|
||||
import com.fsck.k9.view.AttachmentView;
|
||||
import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback;
|
||||
import com.fsck.k9.view.MessageHeader;
|
||||
@ -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();
|
||||
@ -845,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();
|
||||
|
@ -4,6 +4,7 @@ import android.text.*;
|
||||
import android.text.Html.TagHandler;
|
||||
import android.util.Log;
|
||||
import com.fsck.k9.K9;
|
||||
|
||||
import org.xml.sax.XMLReader;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -13,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
|
||||
@ -381,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, "<a href=\"$0\">$0</a>");
|
||||
String prepared = text.replaceAll(BITCOIN_URI_PATTERN, "<a href=\"$0\">$0</a>");
|
||||
|
||||
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) != '@')) {
|
||||
|
@ -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;
|
||||
@ -11,11 +15,23 @@ 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.mailstore.LocalMessage;
|
||||
|
||||
public class MessageHelper {
|
||||
/**
|
||||
* If the number of addresses exceeds this value the addresses aren't
|
||||
* resolved to the names of Android contacts.
|
||||
*
|
||||
* <p>
|
||||
* TODO: This number was chosen arbitrarily and should be determined by
|
||||
* performance tests.
|
||||
* </p>
|
||||
*
|
||||
* @see #toFriendly(Address[], com.fsck.k9.helper.Contacts)
|
||||
*/
|
||||
private static final int TOO_MANY_ADDRESSES = 50;
|
||||
|
||||
private static MessageHelper sInstance;
|
||||
|
||||
@ -32,8 +48,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;
|
||||
@ -53,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();
|
||||
}
|
||||
|
||||
@ -68,14 +86,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().getAccountUuid();
|
||||
target.uri = message.getUri();
|
||||
} catch (MessagingException me) {
|
||||
Log.w(K9.LOG_TAG, "Unable to load message info", me);
|
||||
}
|
||||
@ -86,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;
|
||||
@ -104,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();
|
||||
}
|
||||
}
|
||||
|
@ -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$\\-_.+!*'(),%:@&=]*)?";
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
@ -10,33 +11,15 @@ 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 com.fsck.k9.helper.Contacts;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
|
||||
import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
|
||||
|
||||
public class Address {
|
||||
/**
|
||||
* If the number of addresses exceeds this value the addresses aren't
|
||||
* resolved to the names of Android contacts.
|
||||
*
|
||||
* <p>
|
||||
* TODO: This number was chosen arbitrarily and should be determined by
|
||||
* performance tests.
|
||||
* </p>
|
||||
*
|
||||
* @see Address#toFriendly(Address[], Contacts)
|
||||
*/
|
||||
private static final int TOO_MANY_ADDRESSES = 50;
|
||||
private static final Pattern ATOM = Pattern.compile("^(?:[a-zA-Z0-9!#$%&'*+\\-/=?^_`{|}~]|\\s)+$");
|
||||
|
||||
/**
|
||||
* Immutable empty {@link Address} array
|
||||
@ -162,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));
|
||||
}
|
||||
@ -198,7 +181,7 @@ public class Address {
|
||||
@Override
|
||||
public String toString() {
|
||||
if (!TextUtils.isEmpty(mPersonal)) {
|
||||
return Utility.quoteAtoms(mPersonal) + " <" + mAddress + ">";
|
||||
return quoteAtoms(mPersonal) + " <" + mAddress + ">";
|
||||
} else {
|
||||
return mAddress;
|
||||
}
|
||||
@ -233,77 +216,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((Contacts)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 Contacts 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) {
|
||||
return toFriendly(addresses, null);
|
||||
}
|
||||
|
||||
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(addresses[i].toFriendly(contacts));
|
||||
if (i < addresses.length - 1) {
|
||||
sb.append(',');
|
||||
}
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks an address list previously packed with packAddressList()
|
||||
@ -368,4 +280,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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}.
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
package com.fsck.k9.mail;
|
||||
|
||||
|
||||
public abstract class BodyPart implements Part {
|
||||
private Multipart mParent;
|
||||
|
||||
@ -13,7 +13,4 @@ public abstract class BodyPart implements Part {
|
||||
}
|
||||
|
||||
public abstract void setEncoding(String encoding) throws MessagingException;
|
||||
|
||||
@Override
|
||||
public abstract void setUsing7bitTransport() throws MessagingException;
|
||||
}
|
||||
|
@ -1,21 +1,15 @@
|
||||
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;
|
||||
|
||||
import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
|
||||
|
||||
public abstract class Folder {
|
||||
protected final Account mAccount;
|
||||
|
||||
public abstract class Folder<T extends Message> {
|
||||
private String status = null;
|
||||
private long lastChecked = 0;
|
||||
private long lastPush = 0;
|
||||
@ -32,10 +26,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 +73,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 +84,7 @@ public abstract class Folder {
|
||||
* @return List of messages
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public abstract List<? extends Message> getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener) throws MessagingException;
|
||||
public abstract List<T> getMessages(int start, int end, Date earliestDate, MessageRetrievalListener<T> listener) throws MessagingException;
|
||||
|
||||
/**
|
||||
* Fetches the given list of messages. The specified listener is notified as
|
||||
@ -104,13 +94,13 @@ public abstract class Folder {
|
||||
* @param listener Listener to notify as we download messages.
|
||||
* @return List of messages
|
||||
*/
|
||||
public abstract List<? extends Message> getMessages(MessageRetrievalListener listener) throws MessagingException;
|
||||
public abstract List<T> getMessages(MessageRetrievalListener<T> listener) throws MessagingException;
|
||||
|
||||
public List<? extends Message> getMessages(MessageRetrievalListener listener, boolean includeDeleted) throws MessagingException {
|
||||
public List<T> getMessages(MessageRetrievalListener<T> listener, boolean includeDeleted) throws MessagingException {
|
||||
return getMessages(listener);
|
||||
}
|
||||
|
||||
public abstract List<? extends Message> getMessages(String[] uids, MessageRetrievalListener listener)
|
||||
public abstract List<T> getMessages(String[] uids, MessageRetrievalListener<T> listener)
|
||||
throws MessagingException;
|
||||
|
||||
public abstract Map<String, String> appendMessages(List<? extends Message> messages) throws MessagingException;
|
||||
@ -149,15 +139,15 @@ public abstract class Folder {
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public abstract void fetch(List<? extends Message> messages, FetchProfile fp,
|
||||
MessageRetrievalListener listener) throws MessagingException;
|
||||
MessageRetrievalListener<T> listener) throws MessagingException;
|
||||
|
||||
public void fetchPart(Message message, Part part,
|
||||
MessageRetrievalListener listener) throws MessagingException {
|
||||
MessageRetrievalListener<T> listener) throws MessagingException {
|
||||
// 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;
|
||||
@ -225,10 +215,6 @@ public abstract class Folder {
|
||||
return getSyncClass();
|
||||
}
|
||||
|
||||
public void refresh(Preferences preferences) throws MessagingException {
|
||||
|
||||
}
|
||||
|
||||
public boolean isInTopGroup() {
|
||||
return false;
|
||||
}
|
||||
@ -241,10 +227,6 @@ public abstract class Folder {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
return mAccount;
|
||||
}
|
||||
|
||||
public List<Message> search(String queryString, final Set<Flag> requiredFlags, final Set<Flag> forbiddenFlags)
|
||||
throws MessagingException {
|
||||
throw new MessagingException("K-9 does not support searches on this folder type");
|
||||
|
44
src/com/fsck/k9/mail/K9MailLib.java
Normal file
44
src/com/fsck/k9/mail/K9MailLib.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
|
||||
|
||||
|
||||
public abstract class Message implements Part, CompositeBody {
|
||||
|
||||
private MessageReference mReference = null;
|
||||
|
||||
public enum RecipientType {
|
||||
TO, CC, BCC,
|
||||
}
|
||||
@ -54,9 +48,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())
|
||||
&& mFolder.getAccount().getUuid().equals(other.getFolder().getAccount().getUuid()));
|
||||
return (getUid().equals(other.getUid())
|
||||
&& getFolder().getName().equals(other.getFolder().getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -65,7 +58,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 +67,6 @@ public abstract class Message implements Part, CompositeBody {
|
||||
}
|
||||
|
||||
public void setUid(String uid) {
|
||||
mReference = null;
|
||||
this.mUid = uid;
|
||||
}
|
||||
|
||||
@ -97,7 +88,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;
|
||||
|
||||
@ -126,28 +117,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<String> getHeaderNames() throws UnavailableStorageException;
|
||||
|
||||
@Override
|
||||
public abstract void removeHeader(String name) throws MessagingException;
|
||||
|
||||
@Override
|
||||
public abstract void setBody(Body body) throws MessagingException;
|
||||
public abstract Set<String> getHeaderNames() throws MessagingException;
|
||||
|
||||
public abstract long getId();
|
||||
|
||||
@ -249,20 +219,10 @@ 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.uid = mUid;
|
||||
}
|
||||
return mReference;
|
||||
}
|
||||
|
||||
public long calculateSize() {
|
||||
try {
|
||||
|
||||
@ -272,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;
|
||||
}
|
||||
@ -282,14 +242,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);
|
||||
@ -308,6 +266,5 @@ public abstract class Message implements Part, CompositeBody {
|
||||
*/
|
||||
@Override
|
||||
public abstract Message clone();
|
||||
@Override
|
||||
public abstract void setUsing7bitTransport() throws MessagingException;
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
|
||||
package com.fsck.k9.controller;
|
||||
package com.fsck.k9.mail;
|
||||
|
||||
import com.fsck.k9.mail.Message;
|
||||
|
||||
public interface MessageRetrievalListener {
|
||||
public interface MessageRetrievalListener<T extends Message> {
|
||||
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 <strong>this method is almost never invoked by various Stores! Don't rely on it unless fixed!!</strong>
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -5,29 +5,29 @@ 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 getContentId() throws MessagingException;
|
||||
|
||||
public String[] getHeader(String name) throws MessagingException;
|
||||
String[] getHeader(String name) throws MessagingException;
|
||||
|
||||
public boolean isMimeType(String mimeType) throws MessagingException;
|
||||
boolean isMimeType(String mimeType) throws MessagingException;
|
||||
|
||||
public String getMimeType() throws MessagingException;
|
||||
String getMimeType() throws MessagingException;
|
||||
|
||||
public void setBody(Body body) throws MessagingException;
|
||||
void setBody(Body body) throws MessagingException;
|
||||
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException;
|
||||
void writeTo(OutputStream out) throws IOException, MessagingException;
|
||||
|
||||
/**
|
||||
* Called just prior to transmission, once the type of transport is known to
|
||||
@ -41,5 +41,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;
|
||||
}
|
||||
|
@ -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.
|
||||
* </p>
|
||||
*
|
||||
* @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 {
|
||||
/**
|
||||
|
@ -1,24 +1,10 @@
|
||||
|
||||
package com.fsck.k9.mail;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
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 +15,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<String, Store> sStores = new HashMap<String, Store>();
|
||||
|
||||
/**
|
||||
* Local stores indexed by UUID because the Uri may change due to migration to/from SD-card.
|
||||
*/
|
||||
private static ConcurrentMap<String, Store> sLocalStores = new ConcurrentHashMap<String, Store>();
|
||||
|
||||
/**
|
||||
* Lock objects indexed by account UUID.
|
||||
*
|
||||
* @see #getLocalInstance(Account, Application)
|
||||
*/
|
||||
private static ConcurrentMap<String, Object> sAccountLocks = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
/**
|
||||
* 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 <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException;
|
||||
@ -244,14 +45,25 @@ public abstract class Store {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void sendMessages(List<? extends Message> messages) throws MessagingException {
|
||||
}
|
||||
public void sendMessages(List<? extends Message> messages) throws MessagingException { }
|
||||
|
||||
public Pusher getPusher(PushReceiver receiver) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
return mAccount;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,26 @@
|
||||
|
||||
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;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
public abstract class Transport {
|
||||
protected static final int SOCKET_CONNECT_TIMEOUT = 10000;
|
||||
|
||||
// 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);
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
1121
src/com/fsck/k9/mail/internet/CharsetSupport.java
Normal file
1121
src/com/fsck/k9/mail/internet/CharsetSupport.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -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.
|
||||
@ -35,7 +36,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 +69,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,13 +163,13 @@ class DecoderUtil {
|
||||
|
||||
String charset;
|
||||
try {
|
||||
charset = MimeUtility.fixupCharset(mimeCharset, message);
|
||||
charset = CharsetSupport.fixupCharset(mimeCharset, message);
|
||||
} catch (MessagingException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
103
src/com/fsck/k9/mail/internet/JisSupport.java
Normal file
103
src/com/fsck/k9/mail/internet/JisSupport.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
453
src/com/fsck/k9/mail/internet/MessageExtractor.java
Normal file
453
src/com/fsck/k9/mail/internet/MessageExtractor.java
Normal file
@ -0,0 +1,453 @@
|
||||
package com.fsck.k9.mail.internet;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
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.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;
|
||||
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 {
|
||||
private 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("<meta http-equiv=\"?Content-Type\"? content=\"text/html; charset=(.+?)\">", 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(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(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<Viewable> getViewables(Part part, List<Part> attachments) throws MessagingException {
|
||||
List<Viewable> viewables = new ArrayList<Viewable>();
|
||||
|
||||
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<Viewable> text = findTextPart(multipart, true);
|
||||
|
||||
Set<Part> knownTextParts = getParts(text);
|
||||
List<Viewable> 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;
|
||||
}
|
||||
|
||||
public static Set<Part> getTextParts(Part part) throws MessagingException {
|
||||
List<Part> attachments = new ArrayList<Part>();
|
||||
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<Part> collectAttachments(Message message) throws MessagingException {
|
||||
try {
|
||||
List<Part> attachments = new ArrayList<Part>();
|
||||
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<Part> 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)
|
||||
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 Text} viewables.
|
||||
*
|
||||
* @throws MessagingException
|
||||
* In case of an error.
|
||||
*/
|
||||
private static List<Viewable> findTextPart(Multipart multipart, boolean directChild)
|
||||
throws MessagingException {
|
||||
List<Viewable> viewables = new ArrayList<Viewable>();
|
||||
|
||||
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<Viewable> 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<Viewable> findHtmlPart(Multipart multipart, Set<Part> knownTextParts,
|
||||
List<Part> attachments, boolean directChild) throws MessagingException {
|
||||
List<Viewable> viewables = new ArrayList<Viewable>();
|
||||
|
||||
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<Viewable> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<Part> knownTextParts,
|
||||
List<Part> 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 MessageExtractor#findHtmlPart(Multipart, Set, List, boolean)
|
||||
* @see MessageExtractor#findAttachments(Multipart, Set, List)
|
||||
*/
|
||||
private static Set<Part> getParts(List<Viewable> viewables) {
|
||||
Set<Part> parts = new HashSet<Part>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -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)) {
|
||||
|
@ -33,8 +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;
|
||||
|
||||
/**
|
||||
* An implementation of Message that stores all of it's metadata in RFC 822 and
|
||||
@ -138,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"));
|
||||
}
|
||||
|
||||
@ -152,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) {
|
||||
@ -333,7 +331,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 +417,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<String> getHeaderNames() throws UnavailableStorageException {
|
||||
public Set<String> getHeaderNames() throws MessagingException {
|
||||
return mHeader.getHeaderNames();
|
||||
}
|
||||
|
||||
@ -474,7 +472,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);
|
||||
}
|
||||
}
|
||||
@ -608,28 +606,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
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
|
104
src/com/fsck/k9/mail/internet/Viewable.java
Normal file
104
src/com/fsck/k9/mail/internet/Viewable.java
Normal file
@ -0,0 +1,104 @@
|
||||
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 MessageExtractor#getViewables(com.fsck.k9.mail.Part, java.util.List)}
|
||||
*
|
||||
* @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)
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
class Text extends Textual {
|
||||
public Text(Part part) {
|
||||
super(part);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a {@code text/html} part of a message.
|
||||
*/
|
||||
class Html extends Textual {
|
||||
public Html(Part part) {
|
||||
super(part);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a {@code message/rfc822} part of a message.
|
||||
*
|
||||
* <p>
|
||||
* This is used to extract basic header information when the message contents are displayed
|
||||
* inline.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* <p>
|
||||
* Only relevant {@code text/plain} and {@code text/html} children are stored in this container
|
||||
* class.
|
||||
* </p>
|
||||
*/
|
||||
class Alternative implements Viewable {
|
||||
private List<Viewable> mText;
|
||||
private List<Viewable> mHtml;
|
||||
|
||||
public Alternative(List<Viewable> text, List<Viewable> html) {
|
||||
mText = text;
|
||||
mHtml = html;
|
||||
}
|
||||
|
||||
public List<Viewable> getText() {
|
||||
return mText;
|
||||
}
|
||||
|
||||
public List<Viewable> getHtml() {
|
||||
return mHtml;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
|
||||
package com.fsck.k9.net.ssl;
|
||||
package com.fsck.k9.mail.ssl;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.security.Principal;
|
||||
@ -20,16 +20,17 @@ 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.
|
||||
*/
|
||||
public class KeyChainKeyManager extends X509ExtendedKeyManager {
|
||||
class KeyChainKeyManager extends X509ExtendedKeyManager {
|
||||
|
||||
private static PrivateKey sClientCertificateReferenceWorkaround;
|
||||
|
||||
@ -207,10 +208,10 @@ public 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;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.security;
|
||||
package com.fsck.k9.mail.ssl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
|
||||
package com.fsck.k9.net.ssl;
|
||||
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;
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.net.ssl;
|
||||
package com.fsck.k9.mail.ssl;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
@ -293,7 +293,7 @@ public 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
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -15,19 +15,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.fsck.k9.mail.store.imap;
|
||||
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.
|
||||
*/
|
||||
public class ImapUtility {
|
||||
class ImapUtility {
|
||||
/**
|
||||
* Gets all of the values in a sequence set per RFC 3501.
|
||||
*
|
||||
@ -101,12 +101,12 @@ public 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 @@ public class ImapUtility {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
Log.d(K9.LOG_TAG, "Invalid UID value: " + number);
|
||||
Log.d(LOG_TAG, "Invalid UID value: " + number);
|
||||
|
||||
return false;
|
||||
}
|
@ -3,17 +3,14 @@ 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;
|
||||
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.net.ssl.TrustedSocketFactory;
|
||||
import com.fsck.k9.mail.ssl.TrustedSocketFactory;
|
||||
import com.fsck.k9.mail.MessageRetrievalListener;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
@ -24,7 +21,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 +31,10 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class Pop3Store extends Store {
|
||||
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";
|
||||
|
||||
private static final String STLS_COMMAND = "STLS";
|
||||
@ -128,12 +127,12 @@ public class Pop3Store extends Store {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -154,11 +153,11 @@ public class Pop3Store extends Store {
|
||||
* @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) {
|
||||
@ -209,12 +208,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 +242,13 @@ public class Pop3Store extends Store {
|
||||
@Override
|
||||
public List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException {
|
||||
List<Folder> folders = new LinkedList<Folder>();
|
||||
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 +271,7 @@ public class Pop3Store extends Store {
|
||||
return false;
|
||||
}
|
||||
|
||||
class Pop3Folder extends Folder {
|
||||
class Pop3Folder extends Folder<Pop3Message> {
|
||||
private Socket mSocket;
|
||||
private InputStream mIn;
|
||||
private OutputStream mOut;
|
||||
@ -283,11 +282,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 +296,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 +312,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 +327,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()) {
|
||||
@ -461,7 +460,7 @@ public class Pop3Store extends Store {
|
||||
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
|
||||
@ -541,7 +540,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 +558,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);
|
||||
@ -635,7 +634,7 @@ public class Pop3Store extends Store {
|
||||
// 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];
|
||||
@ -693,8 +692,8 @@ public class Pop3Store extends Store {
|
||||
Set<String> unindexedUids = new HashSet<String>();
|
||||
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);
|
||||
}
|
||||
@ -719,8 +718,8 @@ public class Pop3Store extends Store {
|
||||
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);
|
||||
@ -734,8 +733,8 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
|
||||
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);
|
||||
@ -761,7 +760,7 @@ public class Pop3Store extends Store {
|
||||
* @throws MessagingException
|
||||
*/
|
||||
@Override
|
||||
public void fetch(List<? extends Message> messages, FetchProfile fp, MessageRetrievalListener listener)
|
||||
public void fetch(List<? extends Message> messages, FetchProfile fp, MessageRetrievalListener<Pop3Message> listener)
|
||||
throws MessagingException {
|
||||
if (messages == null || messages.isEmpty()) {
|
||||
return;
|
||||
@ -805,9 +804,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 +818,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);
|
||||
@ -906,8 +905,8 @@ public class Pop3Store extends Store {
|
||||
// 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.");
|
||||
}
|
||||
|
||||
@ -921,8 +920,8 @@ public class Pop3Store extends Store {
|
||||
// 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.");
|
||||
}
|
||||
|
||||
@ -1029,8 +1028,8 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
@ -1122,12 +1121,12 @@ public class Pop3Store extends Store {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1199,7 +1198,7 @@ public class Pop3Store extends Store {
|
||||
// }
|
||||
// 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);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
120
src/com/fsck/k9/mail/store/RemoteStore.java
Normal file
120
src/com/fsck/k9/mail/store/RemoteStore.java
Normal file
@ -0,0 +1,120 @@
|
||||
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<String, Store> sStores = new HashMap<String, Store>();
|
||||
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
31
src/com/fsck/k9/mail/store/StoreConfig.java
Normal file
31
src/com/fsck/k9/mail/store/StoreConfig.java
Normal file
@ -0,0 +1,31 @@
|
||||
package com.fsck.k9.mail.store;
|
||||
|
||||
public interface StoreConfig {
|
||||
String getStoreUri();
|
||||
String getTransportUri();
|
||||
|
||||
boolean subscribedFoldersOnly();
|
||||
boolean useCompression(int type);
|
||||
|
||||
String getInboxFolderName();
|
||||
String getOutboxFolderName();
|
||||
String getDraftsFolderName();
|
||||
|
||||
void setDraftsFolderName(String name);
|
||||
void setTrashFolderName(String name);
|
||||
void setSpamFolderName(String name);
|
||||
void setSentFolderName(String name);
|
||||
void setAutoExpandFolderName(String name);
|
||||
void setInboxFolderName(String name);
|
||||
|
||||
int getMaximumAutoDownloadMessageSize();
|
||||
|
||||
boolean allowRemoteSearch();
|
||||
boolean isRemoteSearchFullText();
|
||||
|
||||
boolean isPushPollOnConnect();
|
||||
|
||||
int getDisplayCount();
|
||||
|
||||
int getIdleRefreshMinutes();
|
||||
}
|
@ -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;
|
||||
|
@ -2,15 +2,11 @@ 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;
|
||||
|
||||
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.MessageRetrievalListener;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.*;
|
||||
@ -49,13 +45,16 @@ 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;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Uses WebDAV formatted HTTP calls to an MS Exchange server to fetch email
|
||||
* and email information.
|
||||
* </pre>
|
||||
*/
|
||||
public class WebDavStore extends Store {
|
||||
public class WebDavStore extends RemoteStore {
|
||||
public static final String STORE_TYPE = "WebDAV";
|
||||
|
||||
// Authentication types
|
||||
@ -138,7 +137,7 @@ public class WebDavStore extends Store {
|
||||
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) {
|
||||
@ -147,7 +146,7 @@ public class WebDavStore extends Store {
|
||||
alias = username;
|
||||
}
|
||||
if (userInfoParts.length > 1) {
|
||||
password = UrlEncodingHelper.decodeUtf8(userInfoParts[1]);
|
||||
password = decodeUtf8(userInfoParts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,9 +186,9 @@ public class WebDavStore extends Store {
|
||||
* @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) {
|
||||
@ -294,12 +293,12 @@ public class WebDavStore extends Store {
|
||||
private Map<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
@ -340,7 +339,7 @@ public class WebDavStore extends Store {
|
||||
// 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() {
|
||||
@ -380,21 +379,21 @@ public class WebDavStore extends Store {
|
||||
Map<String, String> 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)
|
||||
@ -475,7 +474,7 @@ public class WebDavStore extends Store {
|
||||
|
||||
// Decodes the url-encoded folder name (i.e. "My%20folder" => "My Folder"
|
||||
|
||||
return UrlEncodingHelper.decodeUtf8(fullPathName);
|
||||
return decodeUtf8(fullPathName);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -721,7 +720,7 @@ public class WebDavStore extends Store {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -791,7 +790,7 @@ public class WebDavStore extends Store {
|
||||
} 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);
|
||||
}
|
||||
|
||||
@ -893,7 +892,7 @@ public class WebDavStore extends Store {
|
||||
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 {
|
||||
@ -987,7 +986,7 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
@ -1024,10 +1023,10 @@ public class WebDavStore extends Store {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1094,10 +1093,10 @@ public class WebDavStore extends Store {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1122,8 +1121,8 @@ public class WebDavStore extends Store {
|
||||
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 + "'");
|
||||
}
|
||||
|
||||
@ -1155,10 +1154,10 @@ public class WebDavStore extends Store {
|
||||
|
||||
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);
|
||||
}
|
||||
@ -1166,10 +1165,10 @@ public class WebDavStore extends Store {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1196,7 +1195,7 @@ public class WebDavStore extends Store {
|
||||
|
||||
@Override
|
||||
public void sendMessages(List<? extends Message> 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<? extends Message> retMessages = tmpFolder.appendWebDavMessages(messages);
|
||||
@ -1216,7 +1215,7 @@ public class WebDavStore extends Store {
|
||||
/**
|
||||
* A WebDav Folder
|
||||
*/
|
||||
class WebDavFolder extends Folder {
|
||||
class WebDavFolder extends Folder<WebDavMessage> {
|
||||
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;
|
||||
|
||||
@ -1238,9 +1237,9 @@ public class WebDavStore extends Store {
|
||||
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;
|
||||
@ -1310,7 +1309,7 @@ public class WebDavStore extends Store {
|
||||
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);
|
||||
}
|
||||
@ -1333,8 +1332,8 @@ public class WebDavStore extends Store {
|
||||
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;
|
||||
@ -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<? extends Message> messages, FetchProfile fp, MessageRetrievalListener listener)
|
||||
public void fetch(List<? extends Message> messages, FetchProfile fp, MessageRetrievalListener<WebDavMessage> 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<? extends Message> messages, MessageRetrievalListener listener, int lines)
|
||||
private void fetchMessages(List<? extends Message> messages, MessageRetrievalListener<WebDavMessage> listener, int lines)
|
||||
throws MessagingException {
|
||||
WebDavHttpClient httpclient;
|
||||
httpclient = getHttpClient();
|
||||
@ -1561,7 +1560,7 @@ public class WebDavStore extends Store {
|
||||
*/
|
||||
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");
|
||||
@ -1569,7 +1568,7 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
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;
|
||||
@ -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;
|
||||
|
||||
@ -1625,13 +1624,13 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
} 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);
|
||||
@ -1702,7 +1701,7 @@ public class WebDavStore extends Store {
|
||||
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) {
|
||||
@ -1771,7 +1770,7 @@ public class WebDavStore extends Store {
|
||||
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,9 +1880,9 @@ public class WebDavStore extends Store {
|
||||
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);
|
||||
Log.i(LOG_TAG, "Uploading message as " + messageURL);
|
||||
|
||||
httpmethod = new HttpGeneric(messageURL);
|
||||
httpmethod.setMethod("PUT");
|
||||
@ -1922,7 +1921,7 @@ public class WebDavStore extends Store {
|
||||
|
||||
@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;
|
||||
@ -1930,7 +1929,7 @@ public class WebDavStore extends Store {
|
||||
|
||||
@Override
|
||||
public void setFlags(final Set<Flag> flags, boolean value) throws MessagingException {
|
||||
Log.e(K9.LOG_TAG,
|
||||
Log.e(LOG_TAG,
|
||||
"Unimplemented method setFlags(Set<Flag>, boolean) breaks markAllMessagesAsRead and EmptyTrash");
|
||||
// Try to make this efficient by not retrieving all of the messages
|
||||
}
|
||||
@ -1968,11 +1967,11 @@ public class WebDavStore extends Store {
|
||||
* 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: "
|
||||
Log.e(LOG_TAG, "IllegalArgumentException caught in setUrl: " + iae + "\nTrace: "
|
||||
+ processException(iae));
|
||||
}
|
||||
|
||||
@ -2022,7 +2021,7 @@ public class WebDavStore extends Store {
|
||||
@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));
|
||||
}
|
||||
|
||||
@ -2320,7 +2319,7 @@ public class WebDavStore extends Store {
|
||||
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 {
|
||||
@ -2359,8 +2358,8 @@ public class WebDavStore extends Store {
|
||||
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("/");
|
||||
@ -2373,12 +2372,12 @@ public class WebDavStore extends Store {
|
||||
*/
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
|
||||
@ -2389,13 +2388,13 @@ public class WebDavStore extends Store {
|
||||
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));
|
||||
}
|
||||
|
||||
@ -2425,7 +2424,7 @@ public class WebDavStore extends Store {
|
||||
* 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");
|
||||
}
|
||||
|
||||
@ -2441,7 +2440,7 @@ public class WebDavStore extends Store {
|
||||
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;
|
||||
|
@ -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.local.LocalMessage;
|
||||
import com.fsck.k9.net.ssl.TrustedSocketFactory;
|
||||
import com.fsck.k9.mail.internet.CharsetSupport;
|
||||
import com.fsck.k9.mail.store.StoreConfig;
|
||||
import com.fsck.k9.mail.ssl.TrustedSocketFactory;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
@ -28,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";
|
||||
|
||||
@ -95,19 +96,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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -124,16 +125,16 @@ 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) {
|
||||
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) {
|
||||
@ -149,7 +150,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) {
|
||||
@ -183,10 +184,10 @@ public class SmtpTransport extends Transport {
|
||||
private boolean m8bitEncodingAllowed;
|
||||
private int mLargestAcceptableMessage;
|
||||
|
||||
public SmtpTransport(Account 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);
|
||||
}
|
||||
@ -305,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -438,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;
|
||||
@ -465,7 +466,7 @@ public class SmtpTransport extends Transport {
|
||||
new HashMap<String, List<String>>();
|
||||
for (Address address : addresses) {
|
||||
String addressString = address.getAddress();
|
||||
String charset = MimeUtility.getCharsetFromAddress(addressString);
|
||||
String charset = CharsetSupport.getCharsetFromAddress(addressString);
|
||||
List<String> addressesOfCharset = charsetAddressesMap.get(charset);
|
||||
if (addressesOfCharset == null) {
|
||||
addressesOfCharset = new ArrayList<String>();
|
||||
@ -495,7 +496,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?
|
||||
@ -529,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;
|
||||
}
|
||||
|
||||
@ -582,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();
|
||||
@ -701,8 +702,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
|
||||
@ -716,7 +717,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) {
|
||||
@ -756,7 +757,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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,16 +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.mail.K9MailLib;
|
||||
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;
|
||||
|
||||
import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
|
||||
|
||||
public class WebDavTransport extends Transport {
|
||||
public static final String TRANSPORT_TYPE = WebDavStore.STORE_TYPE;
|
||||
|
||||
@ -43,21 +45,17 @@ 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 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();
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileNotFoundException;
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import com.fsck.k9.mail.Body;
|
||||
import com.fsck.k9.mail.MessagingException;
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@ -28,11 +28,11 @@ 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;
|
||||
import com.fsck.k9.controller.MessageRemovalListener;
|
||||
import com.fsck.k9.controller.MessageRetrievalListener;
|
||||
import com.fsck.k9.mail.MessageRetrievalListener;
|
||||
import com.fsck.k9.helper.HtmlConverter;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.Address;
|
||||
@ -51,14 +51,12 @@ 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.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.mailstore.LockableDatabase.DbCallback;
|
||||
import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
|
||||
import com.fsck.k9.provider.AttachmentProvider;
|
||||
|
||||
public class LocalFolder extends Folder implements Serializable {
|
||||
|
||||
public class LocalFolder extends Folder<LocalMessage> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -1973296520918624767L;
|
||||
|
||||
@ -80,22 +78,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 +99,23 @@ public class LocalFolder extends Folder implements Serializable {
|
||||
return mFolderId;
|
||||
}
|
||||
|
||||
public String getAccountUuid()
|
||||
{
|
||||
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 +228,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 +529,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 +609,7 @@ public class LocalFolder extends Folder implements Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(final List<? extends Message> messages, final FetchProfile fp, final MessageRetrievalListener listener)
|
||||
public void fetch(final List<? extends Message> messages, final FetchProfile fp, final MessageRetrievalListener<LocalMessage> listener)
|
||||
throws MessagingException {
|
||||
try {
|
||||
this.localStore.database.execute(false, new DbCallback<Void>() {
|
||||
@ -614,7 +626,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 +641,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 +712,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);
|
||||
@ -813,10 +825,10 @@ public class LocalFolder extends Folder implements Serializable {
|
||||
* The messages whose headers should be loaded.
|
||||
* @throws UnavailableStorageException
|
||||
*/
|
||||
void populateHeaders(final List<LocalMessage> messages) throws UnavailableStorageException {
|
||||
void populateHeaders(final List<LocalMessage> messages) throws MessagingException {
|
||||
this.localStore.database.execute(false, new DbCallback<Void>() {
|
||||
@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;
|
||||
@ -1123,14 +1135,14 @@ public class LocalFolder extends Folder implements Serializable {
|
||||
* @return The local version of the message. Never <code>null</code>.
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public Message storeSmallMessage(final Message message, final Runnable runnable) throws MessagingException {
|
||||
return this.localStore.database.execute(true, new DbCallback<Message>() {
|
||||
public LocalMessage storeSmallMessage(final Message message, final Runnable runnable) throws MessagingException {
|
||||
return this.localStore.database.execute(true, new DbCallback<LocalMessage>() {
|
||||
@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);
|
||||
@ -1287,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;
|
||||
@ -1400,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<Part> attachments = container.attachments;
|
||||
String text = container.text;
|
||||
@ -1439,12 +1451,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) {
|
||||
@ -1473,7 +1485,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<Void>() {
|
||||
@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()) {
|
||||
@ -1502,7 +1514,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<Void>() {
|
||||
@Override
|
||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
||||
@ -1637,7 +1649,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 +1724,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 +1841,7 @@ public class LocalFolder extends Folder implements Serializable {
|
||||
setPushState(null);
|
||||
setLastPush(0);
|
||||
setLastChecked(0);
|
||||
setVisibleLimit(mAccount.getDisplayCount());
|
||||
setVisibleLimit(getAccount().getDisplayCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1878,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 = mAccount.getUuid();
|
||||
String accountUuid = getAccountUuid();
|
||||
Context context = LocalFolder.this.localStore.mApplication;
|
||||
|
||||
// Get attachment IDs
|
||||
@ -2039,7 +2051,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 +2206,8 @@ public class LocalFolder extends Folder implements Serializable {
|
||||
throw(MessagingException) e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
private Account getAccount() {
|
||||
return localStore.getAccount();
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@ -12,23 +12,25 @@ 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;
|
||||
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.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.mailstore.LockableDatabase.DbCallback;
|
||||
import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
|
||||
|
||||
public class LocalMessage extends MimeMessage {
|
||||
|
||||
protected MessageReference mReference;
|
||||
private final LocalStore localStore;
|
||||
|
||||
long mId;
|
||||
private long mId;
|
||||
private int mAttachmentCount;
|
||||
private String mSubject;
|
||||
|
||||
@ -50,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);
|
||||
|
||||
@ -129,7 +130,7 @@ public class LocalMessage extends MimeMessage {
|
||||
}
|
||||
} else {
|
||||
// We successfully found an HTML part; do the necessary character set decoding.
|
||||
text = MimeUtility.getTextFromPart(part);
|
||||
text = MessageExtractor.getTextFromPart(this);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
@ -156,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);
|
||||
@ -189,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);
|
||||
@ -492,44 +499,44 @@ public class LocalMessage extends MimeMessage {
|
||||
db.delete("threads", "message_id = ?", idArg);
|
||||
}
|
||||
|
||||
private void loadHeaders() throws UnavailableStorageException {
|
||||
private void loadHeaders() throws MessagingException {
|
||||
List<LocalMessage> messages = new ArrayList<LocalMessage>();
|
||||
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<String> getHeaderNames() throws UnavailableStorageException {
|
||||
public Set<String> getHeaderNames() throws MessagingException {
|
||||
if (!mHeadersLoaded)
|
||||
loadHeaders();
|
||||
return super.getHeaderNames();
|
||||
@ -557,4 +564,56 @@ public class LocalMessage extends MimeMessage {
|
||||
public long getRootId() {
|
||||
return mRootId;
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
return localStore.getAccount();
|
||||
}
|
||||
|
||||
public MessageReference makeMessageReference() {
|
||||
if (mReference == null) {
|
||||
mReference = new MessageReference();
|
||||
mReference.folderName = getFolder().getName();
|
||||
mReference.uid = mUid;
|
||||
mReference.accountUuid = getFolder().getAccountUuid();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 + (getAccountUuid() != null ? getAccountUuid().hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getAccountUuid() {
|
||||
return getAccount().getUuid();
|
||||
}
|
||||
}
|
470
src/com/fsck/k9/mailstore/LocalMessageExtractor.java
Normal file
470
src/com/fsck/k9/mailstore/LocalMessageExtractor.java
Normal file
@ -0,0 +1,470 @@
|
||||
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;
|
||||
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 =
|
||||
"------------------------------------------------------------------------";
|
||||
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();
|
||||
|
||||
private LocalMessageExtractor() {}
|
||||
/**
|
||||
* 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<Part> attachments = new ArrayList<Part>();
|
||||
|
||||
// Collect all viewable parts
|
||||
List<Viewable> 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 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<Viewable> textAlternative = alternative.getText().isEmpty() ?
|
||||
alternative.getHtml() : alternative.getText();
|
||||
List<Viewable> 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<Part>());
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
return extractTextual(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the contents of a {@link com.fsck.k9.mail.internet.Viewable} to create the HTML to be displayed.
|
||||
*
|
||||
* <p>
|
||||
* This will use {@link com.fsck.k9.helper.HtmlConverter#textToHtml(String)} to convert plain text parts
|
||||
* to HTML if necessary.
|
||||
* </p>
|
||||
*
|
||||
* @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 = MessageExtractor.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<Viewable> 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 Textual) {
|
||||
Part part = ((Textual)viewable).getPart();
|
||||
addTextDivider(text, part, prependDivider);
|
||||
|
||||
String t = MessageExtractor.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<Viewable> 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("<p style=\"margin-top: 2.5em; margin-bottom: 1em; border-bottom: 1px solid #000\">");
|
||||
html.append(filename);
|
||||
html.append("</p>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: <sender>
|
||||
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: <recipients>
|
||||
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: <recipients>
|
||||
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 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: <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("<table style=\"border: 0\">");
|
||||
|
||||
// From: <sender>
|
||||
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: <recipients>
|
||||
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: <recipients>
|
||||
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 date = message.getSentDate();
|
||||
if (date != null) {
|
||||
addTableRow(html, context.getString(R.string.message_compose_quote_header_send_date),
|
||||
date.toString());
|
||||
}
|
||||
|
||||
// Subject: <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("</table>");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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("<tr><th style=\"text-align: left; vertical-align: top;\">");
|
||||
html.append(header);
|
||||
html.append("</th>");
|
||||
html.append("<td>");
|
||||
html.append(value);
|
||||
html.append("</td></tr>");
|
||||
}
|
||||
|
||||
private static ViewableContainer extractTextual(Part part) throws MessagingException {
|
||||
String text = "";
|
||||
String html = "";
|
||||
List<Part> attachments = new ArrayList<Part>();
|
||||
|
||||
Body firstBody = part.getBody();
|
||||
if (part.isMimeType("text/plain")) {
|
||||
String bodyText = MessageExtractor.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 = MessageExtractor.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);
|
||||
}
|
||||
}
|
@ -1,15 +1,5 @@
|
||||
|
||||
package com.fsck.k9.mail.store.local;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.ContentResolver;
|
||||
@ -21,22 +11,20 @@ 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.MessageRetrievalListener;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Store;
|
||||
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.mailstore.StorageManager.StorageProvider;
|
||||
import com.fsck.k9.mail.store.StoreConfig;
|
||||
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;
|
||||
@ -44,6 +32,18 @@ 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;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
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;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Implements a SQLite database backed local store for Messages.
|
||||
@ -56,6 +56,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<String, Object> sAccountLocks = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
/**
|
||||
* Local stores indexed by UUID because the Uri may change due to migration to/from SD-card.
|
||||
*/
|
||||
private static ConcurrentMap<String, LocalStore> sLocalStores = new ConcurrentHashMap<String, LocalStore>();
|
||||
|
||||
/*
|
||||
* a String containing the columns getMessages expects to work with
|
||||
* in the correct order.
|
||||
@ -138,6 +150,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 +160,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,15 +171,66 @@ 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) {
|
||||
LocalStore 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 store;
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeAccount(Account account) {
|
||||
try {
|
||||
removeInstance(account);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Failed to reset local store for account " + account.getUuid(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeInstance(Account 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();
|
||||
}
|
||||
|
||||
public long getSize() throws UnavailableStorageException {
|
||||
public long getSize() throws MessagingException {
|
||||
|
||||
final StorageManager storageManager = StorageManager.getInstance(mApplication);
|
||||
|
||||
@ -409,7 +473,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<Void>() {
|
||||
@ -421,7 +485,7 @@ public class LocalStore extends Store implements Serializable {
|
||||
});
|
||||
}
|
||||
|
||||
public List<PendingCommand> getPendingCommands() throws UnavailableStorageException {
|
||||
public List<PendingCommand> getPendingCommands() throws MessagingException {
|
||||
return database.execute(false, new DbCallback<List<PendingCommand>>() {
|
||||
@Override
|
||||
public List<PendingCommand> doDbWork(final SQLiteDatabase db) throws WrappedException {
|
||||
@ -454,7 +518,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]);
|
||||
}
|
||||
@ -470,7 +534,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<Void>() {
|
||||
@Override
|
||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
||||
@ -480,7 +544,7 @@ public class LocalStore extends Store implements Serializable {
|
||||
});
|
||||
}
|
||||
|
||||
public void removePendingCommands() throws UnavailableStorageException {
|
||||
public void removePendingCommands() throws MessagingException {
|
||||
database.execute(false, new DbCallback<Void>() {
|
||||
@Override
|
||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
||||
@ -612,7 +676,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<AttachmentInfo>() {
|
||||
@Override
|
||||
public AttachmentInfo doDbWork(final SQLiteDatabase db) throws WrappedException {
|
||||
@ -653,7 +717,7 @@ public class LocalStore extends Store implements Serializable {
|
||||
public String type;
|
||||
}
|
||||
|
||||
public void createFolders(final List<LocalFolder> foldersToCreate, final int visibleLimit) throws UnavailableStorageException {
|
||||
public void createFolders(final List<LocalFolder> foldersToCreate, final int visibleLimit) throws MessagingException {
|
||||
database.execute(true, new DbCallback<Void>() {
|
||||
@Override
|
||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import com.fsck.k9.mail.internet.TextBody;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
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 {
|
||||
* <code>null</code>.
|
||||
* @return Any relevant data. Can be <code>null</code>.
|
||||
* @throws WrappedException
|
||||
* @throws com.fsck.k9.mail.store.UnavailableStorageException
|
||||
* @throws com.fsck.k9.mail.MessagingException
|
||||
* @throws com.fsck.k9.mailstore.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> T execute(final boolean transactional, final DbCallback<T> callback) throws UnavailableStorageException {
|
||||
public <T> T execute(final boolean transactional, final DbCallback<T> callback) throws MessagingException {
|
||||
lockRead();
|
||||
final boolean doTransaction = transactional && inTransaction.get() == null;
|
||||
try {
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.controller;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import com.fsck.k9.mail.Message;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
class ThreadInfo {
|
||||
public final long threadId;
|
@ -1,4 +1,4 @@
|
||||
package com.fsck.k9.mail.store;
|
||||
package com.fsck.k9.mailstore;
|
||||
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
|
34
src/com/fsck/k9/mailstore/ViewableContainer.java
Normal file
34
src/com/fsck/k9/mailstore/ViewableContainer.java
Normal file
@ -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<Part> attachments;
|
||||
|
||||
public ViewableContainer(String text, String html, List<Part> attachments) {
|
||||
this.text = text;
|
||||
this.html = html;
|
||||
this.attachments = attachments;
|
||||
}
|
||||
}
|
@ -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.mailstore.StorageManager;
|
||||
import com.fsck.k9.preferences.Settings.*;
|
||||
|
||||
public class AccountSettings {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -22,12 +22,12 @@ 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.Store;
|
||||
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;
|
||||
|
||||
@ -376,8 +376,8 @@ public class SettingsImporter {
|
||||
|
||||
// Write incoming server settings (storeUri)
|
||||
ServerSettings incoming = new ImportedServerSettings(account.incoming);
|
||||
String storeUri = Store.createStoreUri(incoming);
|
||||
putString(editor, accountKeyPrefix + Account.STORE_URI_KEY, Utility.base64Encode(storeUri));
|
||||
String storeUri = RemoteStore.createStoreUri(incoming);
|
||||
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
|
||||
|
@ -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) {
|
||||
|
@ -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.mailstore.LocalStore;
|
||||
import com.fsck.k9.mailstore.LocalStore.AttachmentInfo;
|
||||
import com.fsck.k9.mailstore.StorageManager;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
@ -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) {
|
||||
|
@ -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.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;
|
||||
@ -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) {
|
||||
|
@ -32,8 +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.LocalMessage;
|
||||
import com.fsck.k9.mail.store.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;
|
||||
@ -200,9 +201,9 @@ public class MessageProvider extends ContentProvider {
|
||||
public static class DeleteUriExtractor implements FieldExtractor<MessageInfoHolder, String> {
|
||||
@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<MessageInfoHolder, String> {
|
||||
@Override
|
||||
public String getField(final MessageInfoHolder source) {
|
||||
return source.message.getFolder().getAccount().getDescription();
|
||||
return source.message.getAccount().getDescription();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AccountColorExtractor implements FieldExtractor<MessageInfoHolder, Integer> {
|
||||
@Override
|
||||
public Integer getField(final MessageInfoHolder source) {
|
||||
return source.message.getFolder().getAccount().getChipColor();
|
||||
return source.message.getAccount().getChipColor();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AccountNumberExtractor implements FieldExtractor<MessageInfoHolder, Integer> {
|
||||
@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<Message> messages) {
|
||||
final String folderName, final List<LocalMessage> messages) {
|
||||
// cache fields into local variables for faster access on JVM without JIT
|
||||
final MessageHelper helper = mMessageHelper;
|
||||
final List<MessageInfoHolder> 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);
|
||||
|
@ -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.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;
|
||||
|
@ -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.mailstore.UnavailableStorageException;
|
||||
|
||||
/**
|
||||
* Service used to upgrade the accounts' databases and/or track the progress of the upgrade.
|
||||
|
@ -10,9 +10,8 @@ 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.mailstore.LocalMessage;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
@ -89,10 +88,10 @@ public class NotificationActionService extends CoreService {
|
||||
|
||||
List<MessageReference> refs =
|
||||
intent.getParcelableArrayListExtra(EXTRA_MESSAGE_LIST);
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
List<LocalMessage> messages = new ArrayList<LocalMessage>();
|
||||
|
||||
for (MessageReference ref : refs) {
|
||||
Message m = ref.restoreToLocalMessage(this);
|
||||
LocalMessage m = ref.restoreToLocalMessage(this);
|
||||
if (m != null) {
|
||||
messages.add(m);
|
||||
}
|
||||
@ -103,10 +102,10 @@ 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, account, message, false, null);
|
||||
Intent i = MessageCompose.getActionReplyIntent(this, message, false, null);
|
||||
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(i);
|
||||
} else {
|
||||
|
@ -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.mailstore.StorageManager;
|
||||
|
||||
/**
|
||||
* That BroadcastReceiver is only interested in UNMOUNT events.
|
||||
|
@ -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.mailstore.StorageManager;
|
||||
|
||||
/**
|
||||
* That BroadcastReceiver is only interested in MOUNT events.
|
||||
|
@ -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.mailstore.LocalAttachmentBodyPart;
|
||||
import com.fsck.k9.provider.AttachmentProvider;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
|
@ -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);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user