mirror of
https://github.com/moparisthebest/k-9
synced 2025-02-17 07:30:16 -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.MessagingException;
|
||||||
import com.fsck.k9.mail.Store;
|
import com.fsck.k9.mail.Store;
|
||||||
import com.fsck.k9.mail.Folder.FolderClass;
|
import com.fsck.k9.mail.Folder.FolderClass;
|
||||||
import com.fsck.k9.mail.store.StorageManager;
|
import com.fsck.k9.mail.filter.Base64;
|
||||||
import com.fsck.k9.mail.store.StorageManager.StorageProvider;
|
import com.fsck.k9.mail.store.RemoteStore;
|
||||||
import com.fsck.k9.mail.store.local.LocalStore;
|
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;
|
||||||
import com.fsck.k9.provider.EmailProvider.StatsColumns;
|
import com.fsck.k9.provider.EmailProvider.StatsColumns;
|
||||||
import com.fsck.k9.search.ConditionsTreeNode;
|
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.Attribute;
|
||||||
import com.fsck.k9.search.SearchSpecification.SearchCondition;
|
import com.fsck.k9.search.SearchSpecification.SearchCondition;
|
||||||
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
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.fsck.k9.view.ColorChip;
|
||||||
import com.larswerkman.colorpicker.ColorPicker;
|
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
|
* 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.
|
* 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)
|
* 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();
|
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());
|
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);
|
mDescription = prefs.getString(mUuid + ".description", null);
|
||||||
mAlwaysBcc = prefs.getString(mUuid + ".alwaysBcc", mAlwaysBcc);
|
mAlwaysBcc = prefs.getString(mUuid + ".alwaysBcc", mAlwaysBcc);
|
||||||
mAutomaticCheckIntervalMinutes = prefs.getInt(mUuid + ".automaticCheckIntervalMinutes", -1);
|
mAutomaticCheckIntervalMinutes = prefs.getInt(mUuid + ".automaticCheckIntervalMinutes", -1);
|
||||||
@ -689,9 +692,9 @@ public class Account implements BaseAccount {
|
|||||||
editor.putString("accountUuids", accountUuids);
|
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 + ".localStorageProvider", mLocalStorageProviderId);
|
||||||
editor.putString(mUuid + ".transportUri", Utility.base64Encode(mTransportUri));
|
editor.putString(mUuid + ".transportUri", Base64.encode(mTransportUri));
|
||||||
editor.putString(mUuid + ".description", mDescription);
|
editor.putString(mUuid + ".description", mDescription);
|
||||||
editor.putString(mUuid + ".alwaysBcc", mAlwaysBcc);
|
editor.putString(mUuid + ".alwaysBcc", mAlwaysBcc);
|
||||||
editor.putInt(mUuid + ".automaticCheckIntervalMinutes", mAutomaticCheckIntervalMinutes);
|
editor.putInt(mUuid + ".automaticCheckIntervalMinutes", mAutomaticCheckIntervalMinutes);
|
||||||
@ -1076,8 +1079,8 @@ public class Account implements BaseAccount {
|
|||||||
return mDraftsFolderName;
|
return mDraftsFolderName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setDraftsFolderName(String draftsFolderName) {
|
public synchronized void setDraftsFolderName(String name) {
|
||||||
mDraftsFolderName = draftsFolderName;
|
mDraftsFolderName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1096,8 +1099,8 @@ public class Account implements BaseAccount {
|
|||||||
return K9.ERROR_FOLDER_NAME;
|
return K9.ERROR_FOLDER_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setSentFolderName(String sentFolderName) {
|
public synchronized void setSentFolderName(String name) {
|
||||||
mSentFolderName = sentFolderName;
|
mSentFolderName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1113,8 +1116,8 @@ public class Account implements BaseAccount {
|
|||||||
return mTrashFolderName;
|
return mTrashFolderName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setTrashFolderName(String trashFolderName) {
|
public synchronized void setTrashFolderName(String name) {
|
||||||
mTrashFolderName = trashFolderName;
|
mTrashFolderName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1145,8 +1148,8 @@ public class Account implements BaseAccount {
|
|||||||
return mSpamFolderName;
|
return mSpamFolderName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setSpamFolderName(String spamFolderName) {
|
public synchronized void setSpamFolderName(String name) {
|
||||||
mSpamFolderName = spamFolderName;
|
mSpamFolderName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1165,8 +1168,8 @@ public class Account implements BaseAccount {
|
|||||||
return mAutoExpandFolderName;
|
return mAutoExpandFolderName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setAutoExpandFolderName(String autoExpandFolderName) {
|
public synchronized void setAutoExpandFolderName(String name) {
|
||||||
mAutoExpandFolderName = autoExpandFolderName;
|
mAutoExpandFolderName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int getAccountNumber() {
|
public synchronized int getAccountNumber() {
|
||||||
@ -1289,11 +1292,11 @@ public class Account implements BaseAccount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LocalStore getLocalStore() throws MessagingException {
|
public LocalStore getLocalStore() throws MessagingException {
|
||||||
return Store.getLocalInstance(this, K9.app);
|
return LocalStore.getInstance(this, K9.app);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Store getRemoteStore() throws MessagingException {
|
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
|
// It'd be great if this actually went into the store implementation
|
||||||
@ -1657,8 +1660,8 @@ public class Account implements BaseAccount {
|
|||||||
return mInboxFolderName;
|
return mInboxFolderName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInboxFolderName(String mInboxFolderName) {
|
public void setInboxFolderName(String name) {
|
||||||
this.mInboxFolderName = mInboxFolderName;
|
this.mInboxFolderName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean syncRemoteDeletions() {
|
public synchronized boolean syncRemoteDeletions() {
|
||||||
|
@ -4,7 +4,19 @@ package com.fsck.k9;
|
|||||||
import android.text.util.Rfc822Tokenizer;
|
import android.text.util.Rfc822Tokenizer;
|
||||||
import android.widget.AutoCompleteTextView.Validator;
|
import android.widget.AutoCompleteTextView.Validator;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class EmailAddressValidator implements Validator {
|
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) {
|
public CharSequence fixText(CharSequence invalidText) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -14,6 +26,6 @@ public class EmailAddressValidator implements Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isValidAddressOnly(CharSequence text) {
|
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.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
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.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.BootReceiver;
|
||||||
import com.fsck.k9.service.MailService;
|
import com.fsck.k9.service.MailService;
|
||||||
import com.fsck.k9.service.ShutdownReceiver;
|
import com.fsck.k9.service.ShutdownReceiver;
|
||||||
@ -136,37 +136,6 @@ public class K9 extends Application {
|
|||||||
*/
|
*/
|
||||||
public static boolean DEBUG = false;
|
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
|
* If this is enabled than logging that normally hides sensitive information
|
||||||
* like passwords will show that information.
|
* like passwords will show that information.
|
||||||
|
@ -13,7 +13,8 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
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.Editor;
|
||||||
import com.fsck.k9.preferences.Storage;
|
import com.fsck.k9.preferences.Storage;
|
||||||
|
|
||||||
@ -121,7 +122,12 @@ public class Preferences {
|
|||||||
accountsInOrder.remove(account);
|
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.deleteCertificates();
|
||||||
account.delete(this);
|
account.delete(this);
|
||||||
|
@ -5,7 +5,6 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
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.activity.setup.WelcomeMessage;
|
||||||
import com.fsck.k9.controller.MessagingController;
|
import com.fsck.k9.controller.MessagingController;
|
||||||
import com.fsck.k9.helper.SizeFormatter;
|
import com.fsck.k9.helper.SizeFormatter;
|
||||||
import com.fsck.k9.helper.Utility;
|
|
||||||
import com.fsck.k9.mail.AuthType;
|
import com.fsck.k9.mail.AuthType;
|
||||||
import com.fsck.k9.mail.ServerSettings;
|
import com.fsck.k9.mail.ServerSettings;
|
||||||
import com.fsck.k9.mail.Store;
|
|
||||||
import com.fsck.k9.mail.Transport;
|
import com.fsck.k9.mail.Transport;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
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.mail.store.WebDavStore;
|
||||||
import com.fsck.k9.preferences.SettingsExporter;
|
import com.fsck.k9.preferences.SettingsExporter;
|
||||||
import com.fsck.k9.preferences.SettingsImportExportException;
|
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) {
|
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());
|
ServerSettings outgoing = Transport.decodeTransportUri(mAccount.getTransportUri());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -992,9 +990,9 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
|
|||||||
if (mIncomingPassword != null) {
|
if (mIncomingPassword != null) {
|
||||||
// Set incoming server password
|
// Set incoming server password
|
||||||
String storeUri = mAccount.getStoreUri();
|
String storeUri = mAccount.getStoreUri();
|
||||||
ServerSettings incoming = Store.decodeStoreUri(storeUri);
|
ServerSettings incoming = RemoteStore.decodeStoreUri(storeUri);
|
||||||
ServerSettings newIncoming = incoming.newPassword(mIncomingPassword);
|
ServerSettings newIncoming = incoming.newPassword(mIncomingPassword);
|
||||||
String newStoreUri = Store.createStoreUri(newIncoming);
|
String newStoreUri = RemoteStore.createStoreUri(newIncoming);
|
||||||
mAccount.setStoreUri(newStoreUri);
|
mAccount.setStoreUri(newStoreUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,22 +280,16 @@ public class ChooseFolder extends K9ListActivity {
|
|||||||
mAccount.getInboxFolderName().equalsIgnoreCase(name)))) {
|
mAccount.getInboxFolderName().equalsIgnoreCase(name)))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
Folder.FolderClass fMode = folder.getDisplayClass();
|
||||||
folder.refresh(prefs);
|
|
||||||
Folder.FolderClass fMode = folder.getDisplayClass();
|
|
||||||
|
|
||||||
if ((aMode == Account.FolderMode.FIRST_CLASS &&
|
if ((aMode == FolderMode.FIRST_CLASS &&
|
||||||
fMode != Folder.FolderClass.FIRST_CLASS) || (
|
fMode != Folder.FolderClass.FIRST_CLASS) || (
|
||||||
aMode == Account.FolderMode.FIRST_AND_SECOND_CLASS &&
|
aMode == FolderMode.FIRST_AND_SECOND_CLASS &&
|
||||||
fMode != Folder.FolderClass.FIRST_CLASS &&
|
fMode != Folder.FolderClass.FIRST_CLASS &&
|
||||||
fMode != Folder.FolderClass.SECOND_CLASS) || (
|
fMode != Folder.FolderClass.SECOND_CLASS) || (
|
||||||
aMode == Account.FolderMode.NOT_SECOND_CLASS &&
|
aMode == FolderMode.NOT_SECOND_CLASS &&
|
||||||
fMode == Folder.FolderClass.SECOND_CLASS)) {
|
fMode == Folder.FolderClass.SECOND_CLASS)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
} catch (MessagingException me) {
|
|
||||||
Log.e(K9.LOG_TAG, "Couldn't get prefs to check for displayability of folder " +
|
|
||||||
folder.getName(), me);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (folder.isInTopGroup()) {
|
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.helper.power.TracingPowerManager.TracingWakeLock;
|
||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mailstore.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
|
||||||
import com.fsck.k9.search.LocalSearch;
|
import com.fsck.k9.search.LocalSearch;
|
||||||
import com.fsck.k9.search.SearchSpecification.Attribute;
|
import com.fsck.k9.search.SearchSpecification.Attribute;
|
||||||
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
||||||
@ -749,20 +748,14 @@ public class FolderList extends K9ListActivity {
|
|||||||
Account.FolderMode aMode = account.getFolderDisplayMode();
|
Account.FolderMode aMode = account.getFolderDisplayMode();
|
||||||
Preferences prefs = Preferences.getPreferences(getApplication().getApplicationContext());
|
Preferences prefs = Preferences.getPreferences(getApplication().getApplicationContext());
|
||||||
for (Folder folder : folders) {
|
for (Folder folder : folders) {
|
||||||
try {
|
Folder.FolderClass fMode = folder.getDisplayClass();
|
||||||
folder.refresh(prefs);
|
|
||||||
|
|
||||||
Folder.FolderClass fMode = folder.getDisplayClass();
|
if ((aMode == FolderMode.FIRST_CLASS && fMode != Folder.FolderClass.FIRST_CLASS)
|
||||||
|
|| (aMode == FolderMode.FIRST_AND_SECOND_CLASS &&
|
||||||
if ((aMode == Account.FolderMode.FIRST_CLASS && fMode != Folder.FolderClass.FIRST_CLASS)
|
fMode != Folder.FolderClass.FIRST_CLASS &&
|
||||||
|| (aMode == Account.FolderMode.FIRST_AND_SECOND_CLASS &&
|
fMode != Folder.FolderClass.SECOND_CLASS)
|
||||||
fMode != Folder.FolderClass.FIRST_CLASS &&
|
|| (aMode == FolderMode.NOT_SECOND_CLASS && fMode == Folder.FolderClass.SECOND_CLASS)) {
|
||||||
fMode != Folder.FolderClass.SECOND_CLASS)
|
continue;
|
||||||
|| (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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FolderInfoHolder holder = null;
|
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?
|
* 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;
|
private static final long serialVersionUID = 2397327034L;
|
||||||
// Default to a headerInsertionPoint at the beginning of the message.
|
// Default to a headerInsertionPoint at the beginning of the message.
|
||||||
private int headerInsertionPoint = 0;
|
private int headerInsertionPoint = 0;
|
||||||
|
@ -89,6 +89,7 @@ import com.fsck.k9.crypto.PgpData;
|
|||||||
import com.fsck.k9.fragment.ProgressDialogFragment;
|
import com.fsck.k9.fragment.ProgressDialogFragment;
|
||||||
import com.fsck.k9.helper.ContactItem;
|
import com.fsck.k9.helper.ContactItem;
|
||||||
import com.fsck.k9.helper.Contacts;
|
import com.fsck.k9.helper.Contacts;
|
||||||
|
import com.fsck.k9.mail.filter.Base64;
|
||||||
import com.fsck.k9.helper.HtmlConverter;
|
import com.fsck.k9.helper.HtmlConverter;
|
||||||
import com.fsck.k9.helper.IdentityHelper;
|
import com.fsck.k9.helper.IdentityHelper;
|
||||||
import com.fsck.k9.helper.Utility;
|
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.MessagingException;
|
||||||
import com.fsck.k9.mail.Multipart;
|
import com.fsck.k9.mail.Multipart;
|
||||||
import com.fsck.k9.mail.Part;
|
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.MimeBodyPart;
|
||||||
import com.fsck.k9.mail.internet.MimeHeader;
|
import com.fsck.k9.mail.internet.MimeHeader;
|
||||||
import com.fsck.k9.mail.internet.MimeMessage;
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
import com.fsck.k9.mail.internet.MimeMultipart;
|
import com.fsck.k9.mail.internet.MimeMultipart;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
import com.fsck.k9.mail.internet.TextBodyBuilder;
|
import com.fsck.k9.mailstore.LocalAttachmentBody;
|
||||||
import com.fsck.k9.mail.store.local.LocalAttachmentBody;
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
import com.fsck.k9.mail.store.local.TempFileBody;
|
import com.fsck.k9.mailstore.TempFileBody;
|
||||||
import com.fsck.k9.mail.store.local.TempFileMessageBody;
|
import com.fsck.k9.mailstore.TempFileMessageBody;
|
||||||
import com.fsck.k9.view.MessageWebView;
|
import com.fsck.k9.view.MessageWebView;
|
||||||
|
|
||||||
import org.apache.james.mime4j.codec.EncoderUtil;
|
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
|
* 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.
|
* the function is reply all instead of simply reply.
|
||||||
* @param context
|
* @param context
|
||||||
* @param account
|
|
||||||
* @param message
|
* @param message
|
||||||
* @param replyAll
|
* @param replyAll
|
||||||
* @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message
|
* @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message
|
||||||
*/
|
*/
|
||||||
public static Intent getActionReplyIntent(
|
public static Intent getActionReplyIntent(
|
||||||
Context context,
|
Context context,
|
||||||
Account account,
|
LocalMessage message,
|
||||||
Message message,
|
boolean replyAll,
|
||||||
boolean replyAll,
|
String messageBody) {
|
||||||
String messageBody) {
|
|
||||||
Intent i = new Intent(context, MessageCompose.class);
|
Intent i = new Intent(context, MessageCompose.class);
|
||||||
i.putExtra(EXTRA_MESSAGE_BODY, messageBody);
|
i.putExtra(EXTRA_MESSAGE_BODY, messageBody);
|
||||||
i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference());
|
i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference());
|
||||||
@ -468,25 +468,22 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||||||
*/
|
*/
|
||||||
public static void actionReply(
|
public static void actionReply(
|
||||||
Context context,
|
Context context,
|
||||||
Account account,
|
LocalMessage message,
|
||||||
Message message,
|
|
||||||
boolean replyAll,
|
boolean replyAll,
|
||||||
String messageBody) {
|
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.
|
* Compose a new message as a forward of the given message.
|
||||||
* @param context
|
* @param context
|
||||||
* @param account
|
|
||||||
* @param message
|
* @param message
|
||||||
* @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message
|
* @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message
|
||||||
*/
|
*/
|
||||||
public static void actionForward(
|
public static void actionForward(
|
||||||
Context context,
|
Context context,
|
||||||
Account account,
|
LocalMessage message,
|
||||||
Message message,
|
String messageBody) {
|
||||||
String messageBody) {
|
|
||||||
Intent i = new Intent(context, MessageCompose.class);
|
Intent i = new Intent(context, MessageCompose.class);
|
||||||
i.putExtra(EXTRA_MESSAGE_BODY, messageBody);
|
i.putExtra(EXTRA_MESSAGE_BODY, messageBody);
|
||||||
i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference());
|
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 {
|
private MimeMessage createMessage(boolean isDraft) throws MessagingException {
|
||||||
MimeMessage message = new MimeMessage();
|
MimeMessage message = new MimeMessage();
|
||||||
message.addSentDate(new Date());
|
message.addSentDate(new Date(), K9.hideTimeZone());
|
||||||
Address from = new Address(mIdentity.getEmail(), mIdentity.getName());
|
Address from = new Address(mIdentity.getEmail(), mIdentity.getName());
|
||||||
message.setFrom(from);
|
message.setFrom(from);
|
||||||
message.setRecipients(RecipientType.TO, getAddresses(mToView));
|
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.
|
// First item is the body length. We use this to separate the composed reply from the quoted text.
|
||||||
if (tokenizer.hasMoreTokens()) {
|
if (tokenizer.hasMoreTokens()) {
|
||||||
String bodyLengthS = Utility.base64Decode(tokenizer.nextToken());
|
String bodyLengthS = Base64.decode(tokenizer.nextToken());
|
||||||
try {
|
try {
|
||||||
identity.put(IdentityField.LENGTH, Integer.valueOf(bodyLengthS).toString());
|
identity.put(IdentityField.LENGTH, Integer.valueOf(bodyLengthS).toString());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -1666,16 +1663,16 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tokenizer.hasMoreTokens()) {
|
if (tokenizer.hasMoreTokens()) {
|
||||||
identity.put(IdentityField.SIGNATURE, Utility.base64Decode(tokenizer.nextToken()));
|
identity.put(IdentityField.SIGNATURE, Base64.decode(tokenizer.nextToken()));
|
||||||
}
|
}
|
||||||
if (tokenizer.hasMoreTokens()) {
|
if (tokenizer.hasMoreTokens()) {
|
||||||
identity.put(IdentityField.NAME, Utility.base64Decode(tokenizer.nextToken()));
|
identity.put(IdentityField.NAME, Base64.decode(tokenizer.nextToken()));
|
||||||
}
|
}
|
||||||
if (tokenizer.hasMoreTokens()) {
|
if (tokenizer.hasMoreTokens()) {
|
||||||
identity.put(IdentityField.EMAIL, Utility.base64Decode(tokenizer.nextToken()));
|
identity.put(IdentityField.EMAIL, Base64.decode(tokenizer.nextToken()));
|
||||||
}
|
}
|
||||||
if (tokenizer.hasMoreTokens()) {
|
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
|
* @param message
|
||||||
* The source message used to populate the various text fields.
|
* The source message used to populate the various text fields.
|
||||||
*/
|
*/
|
||||||
private void processSourceMessage(Message message) {
|
private void processSourceMessage(LocalMessage message) {
|
||||||
try {
|
try {
|
||||||
switch (mAction) {
|
switch (mAction) {
|
||||||
case REPLY:
|
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";
|
String showQuotedTextMode = "NONE";
|
||||||
|
|
||||||
mDraftId = MessagingController.getInstance(getApplication()).getId(message);
|
mDraftId = MessagingController.getInstance(getApplication()).getId(message);
|
||||||
@ -2852,7 +2849,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||||||
newIdentity.setSignature(k9identity.get(IdentityField.SIGNATURE));
|
newIdentity.setSignature(k9identity.get(IdentityField.SIGNATURE));
|
||||||
mSignatureChanged = true;
|
mSignatureChanged = true;
|
||||||
} else {
|
} else {
|
||||||
newIdentity.setSignatureUse(message.getFolder().getAccount().getSignatureUse());
|
newIdentity.setSignatureUse(message.getFolder().getSignatureUse());
|
||||||
newIdentity.setSignature(mIdentity.getSignature());
|
newIdentity.setSignature(mIdentity.getSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2962,7 +2959,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||||||
Part part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
Part part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||||
if (part != null) { // Shouldn't happen if we were the one who saved it.
|
if (part != null) { // Shouldn't happen if we were the one who saved it.
|
||||||
mQuotedTextFormat = SimpleMessageFormat.HTML;
|
mQuotedTextFormat = SimpleMessageFormat.HTML;
|
||||||
String text = MimeUtility.getTextFromPart(part);
|
String text = MessageExtractor.getTextFromPart(part);
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + ".");
|
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 {
|
boolean viewMessageContent) throws MessagingException {
|
||||||
Part textPart = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
Part textPart = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
||||||
if (textPart != null) {
|
if (textPart != null) {
|
||||||
String text = MimeUtility.getTextFromPart(textPart);
|
String text = MessageExtractor.getTextFromPart(textPart);
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "Loading message with offset " + bodyOffset + ", length " + bodyLength + ". Text length is " + text.length() + ".");
|
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) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, HTML found.");
|
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, HTML found.");
|
||||||
}
|
}
|
||||||
return MimeUtility.getTextFromPart(part);
|
return MessageExtractor.getTextFromPart(part);
|
||||||
}
|
}
|
||||||
|
|
||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
||||||
@ -3238,7 +3235,8 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: HTML requested, text found.");
|
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) {
|
} else if (format == SimpleMessageFormat.TEXT) {
|
||||||
// Text takes precedence, then html.
|
// Text takes precedence, then html.
|
||||||
@ -3247,7 +3245,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, text found.");
|
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, text found.");
|
||||||
}
|
}
|
||||||
return MimeUtility.getTextFromPart(part);
|
return MessageExtractor.getTextFromPart(part);
|
||||||
}
|
}
|
||||||
|
|
||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||||
@ -3255,7 +3253,8 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "getBodyTextFromMessage: Text requested, HTML found.");
|
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();
|
updateMessageFormat();
|
||||||
} else {
|
} else {
|
||||||
processSourceMessage(message);
|
processSourceMessage((LocalMessage) message);
|
||||||
mSourceProcessed = true;
|
mSourceProcessed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package com.fsck.k9.activity;
|
package com.fsck.k9.activity;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import com.fsck.k9.mail.Message;
|
|
||||||
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
|
|
||||||
public class MessageInfoHolder {
|
public class MessageInfoHolder {
|
||||||
public String date;
|
public String date;
|
||||||
@ -18,7 +19,7 @@ public class MessageInfoHolder {
|
|||||||
public boolean forwarded;
|
public boolean forwarded;
|
||||||
public boolean flagged;
|
public boolean flagged;
|
||||||
public boolean dirty;
|
public boolean dirty;
|
||||||
public Message message;
|
public LocalMessage message;
|
||||||
public FolderInfoHolder folder;
|
public FolderInfoHolder folder;
|
||||||
public boolean selected;
|
public boolean selected;
|
||||||
public String account;
|
public String account;
|
||||||
@ -31,7 +32,7 @@ public class MessageInfoHolder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (o instanceof MessageInfoHolder == false) {
|
if (!(o instanceof MessageInfoHolder)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MessageInfoHolder other = (MessageInfoHolder)o;
|
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.MessageListFragment.MessageListFragmentListener;
|
||||||
import com.fsck.k9.fragment.MessageViewFragment;
|
import com.fsck.k9.fragment.MessageViewFragment;
|
||||||
import com.fsck.k9.fragment.MessageViewFragment.MessageViewFragmentListener;
|
import com.fsck.k9.fragment.MessageViewFragment.MessageViewFragmentListener;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mailstore.StorageManager;
|
||||||
import com.fsck.k9.mail.store.StorageManager;
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
import com.fsck.k9.search.LocalSearch;
|
import com.fsck.k9.search.LocalSearch;
|
||||||
import com.fsck.k9.search.SearchAccount;
|
import com.fsck.k9.search.SearchAccount;
|
||||||
import com.fsck.k9.search.SearchSpecification;
|
import com.fsck.k9.search.SearchSpecification;
|
||||||
@ -1193,23 +1193,23 @@ public class MessageList extends K9Activity implements MessageListFragmentListen
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResendMessage(Message message) {
|
public void onResendMessage(LocalMessage message) {
|
||||||
MessageCompose.actionEditDraft(this, message.makeMessageReference());
|
MessageCompose.actionEditDraft(this, message.makeMessageReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onForward(Message message) {
|
public void onForward(LocalMessage message) {
|
||||||
MessageCompose.actionForward(this, message.getFolder().getAccount(), message, null);
|
MessageCompose.actionForward(this, message, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReply(Message message) {
|
public void onReply(LocalMessage message) {
|
||||||
MessageCompose.actionReply(this, message.getFolder().getAccount(), message, false, null);
|
MessageCompose.actionReply(this, message, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReplyAll(Message message) {
|
public void onReplyAll(LocalMessage message) {
|
||||||
MessageCompose.actionReply(this, message.getFolder().getAccount(), message, true, null);
|
MessageCompose.actionReply(this, message, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1399,18 +1399,18 @@ public class MessageList extends K9Activity implements MessageListFragmentListen
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReply(Message message, PgpData pgpData) {
|
public void onReply(LocalMessage message, PgpData pgpData) {
|
||||||
MessageCompose.actionReply(this, mAccount, message, false, pgpData.getDecryptedData());
|
MessageCompose.actionReply(this, message, false, pgpData.getDecryptedData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReplyAll(Message message, PgpData pgpData) {
|
public void onReplyAll(LocalMessage message, PgpData pgpData) {
|
||||||
MessageCompose.actionReply(this, mAccount, message, true, pgpData.getDecryptedData());
|
MessageCompose.actionReply(this, message, true, pgpData.getDecryptedData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onForward(Message mMessage, PgpData mPgpData) {
|
public void onForward(LocalMessage mMessage, PgpData mPgpData) {
|
||||||
MessageCompose.actionForward(this, mAccount, mMessage, mPgpData.getDecryptedData());
|
MessageCompose.actionForward(this, mMessage, mPgpData.getDecryptedData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -8,11 +8,11 @@ import android.util.Log;
|
|||||||
import com.fsck.k9.Account;
|
import com.fsck.k9.Account;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.Preferences;
|
import com.fsck.k9.Preferences;
|
||||||
import com.fsck.k9.helper.Utility;
|
|
||||||
import com.fsck.k9.mail.Flag;
|
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.MessagingException;
|
||||||
|
import com.fsck.k9.mailstore.LocalFolder;
|
||||||
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
|
import com.fsck.k9.mail.filter.Base64;
|
||||||
|
|
||||||
import java.util.StringTokenizer;
|
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.
|
// Split the identity, stripping away the first two characters representing the version and delimiter.
|
||||||
StringTokenizer tokens = new StringTokenizer(identity.substring(2), IDENTITY_SEPARATOR, false);
|
StringTokenizer tokens = new StringTokenizer(identity.substring(2), IDENTITY_SEPARATOR, false);
|
||||||
if (tokens.countTokens() >= 3) {
|
if (tokens.countTokens() >= 3) {
|
||||||
accountUuid = Utility.base64Decode(tokens.nextToken());
|
accountUuid = Base64.decode(tokens.nextToken());
|
||||||
folderName = Utility.base64Decode(tokens.nextToken());
|
folderName = Base64.decode(tokens.nextToken());
|
||||||
uid = Utility.base64Decode(tokens.nextToken());
|
uid = Base64.decode(tokens.nextToken());
|
||||||
|
|
||||||
if (tokens.hasMoreTokens()) {
|
if (tokens.hasMoreTokens()) {
|
||||||
final String flagString = tokens.nextToken();
|
final String flagString = tokens.nextToken();
|
||||||
@ -80,11 +80,11 @@ public class MessageReference implements Parcelable {
|
|||||||
|
|
||||||
refString.append(IDENTITY_VERSION_1);
|
refString.append(IDENTITY_VERSION_1);
|
||||||
refString.append(IDENTITY_SEPARATOR);
|
refString.append(IDENTITY_SEPARATOR);
|
||||||
refString.append(Utility.base64Encode(accountUuid));
|
refString.append(Base64.encode(accountUuid));
|
||||||
refString.append(IDENTITY_SEPARATOR);
|
refString.append(IDENTITY_SEPARATOR);
|
||||||
refString.append(Utility.base64Encode(folderName));
|
refString.append(Base64.encode(folderName));
|
||||||
refString.append(IDENTITY_SEPARATOR);
|
refString.append(IDENTITY_SEPARATOR);
|
||||||
refString.append(Utility.base64Encode(uid));
|
refString.append(Base64.encode(uid));
|
||||||
if (flag != null) {
|
if (flag != null) {
|
||||||
refString.append(IDENTITY_SEPARATOR);
|
refString.append(IDENTITY_SEPARATOR);
|
||||||
refString.append(flag.name());
|
refString.append(flag.name());
|
||||||
@ -128,13 +128,13 @@ public class MessageReference implements Parcelable {
|
|||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message restoreToLocalMessage(Context context) {
|
public LocalMessage restoreToLocalMessage(Context context) {
|
||||||
try {
|
try {
|
||||||
Account account = Preferences.getPreferences(context).getAccount(accountUuid);
|
Account account = Preferences.getPreferences(context).getAccount(accountUuid);
|
||||||
if (account != null) {
|
if (account != null) {
|
||||||
Folder folder = account.getLocalStore().getFolder(folderName);
|
LocalFolder folder = account.getLocalStore().getFolder(folderName);
|
||||||
if (folder != null) {
|
if (folder != null) {
|
||||||
Message message = folder.getMessage(uid);
|
LocalMessage message = folder.getMessage(uid);
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
return message;
|
return message;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package com.fsck.k9.mail.internet;
|
package com.fsck.k9.activity;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
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.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 mIncludeQuotedText = true;
|
||||||
private boolean mReplyAfterQuote = false;
|
private boolean mReplyAfterQuote = false;
|
||||||
private boolean mSignatureBeforeQuotedText = false;
|
private boolean mSignatureBeforeQuotedText = false;
|
||||||
@ -27,7 +27,7 @@ public class TextBodyBuilder {
|
|||||||
/**
|
/**
|
||||||
* Build the {@link Body} that will contain the text of the message.
|
* 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.
|
* possibly the quoted original message.
|
||||||
*/
|
*/
|
||||||
public TextBody buildTextHtml() {
|
public TextBody buildTextHtml() {
|
@ -30,7 +30,7 @@ import android.widget.TextView;
|
|||||||
* <li>{@link #actionUpgradeDatabases(Context, Intent)} will call {@link K9#areDatabasesUpToDate()}
|
* <li>{@link #actionUpgradeDatabases(Context, Intent)} will call {@link K9#areDatabasesUpToDate()}
|
||||||
* to check if we already know whether the databases have been upgraded.</li>
|
* 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
|
* <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
|
* is done as an optimization because it's faster than opening all of the accounts' databases
|
||||||
* one by one.</li>
|
* one by one.</li>
|
||||||
* <li>If there was an error reading the cached database version or if it shows the databases need
|
* <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.activity.ManageIdentities;
|
||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.Store;
|
import com.fsck.k9.mail.Store;
|
||||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
import com.fsck.k9.mailstore.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.StorageManager;
|
import com.fsck.k9.mailstore.StorageManager;
|
||||||
import com.fsck.k9.service.MailService;
|
import com.fsck.k9.service.MailService;
|
||||||
|
|
||||||
import org.openintents.openpgp.util.OpenPgpListPreference;
|
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.AuthType;
|
||||||
import com.fsck.k9.mail.ConnectionSecurity;
|
import com.fsck.k9.mail.ConnectionSecurity;
|
||||||
import com.fsck.k9.mail.ServerSettings;
|
import com.fsck.k9.mail.ServerSettings;
|
||||||
import com.fsck.k9.mail.Store;
|
|
||||||
import com.fsck.k9.mail.Transport;
|
import com.fsck.k9.mail.Transport;
|
||||||
import com.fsck.k9.mail.store.ImapStore;
|
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.mail.transport.SmtpTransport;
|
||||||
import com.fsck.k9.view.ClientCertificateSpinner;
|
import com.fsck.k9.view.ClientCertificateSpinner;
|
||||||
import com.fsck.k9.view.ClientCertificateSpinner.OnClientCertificateChangedListener;
|
import com.fsck.k9.view.ClientCertificateSpinner.OnClientCertificateChangedListener;
|
||||||
@ -421,7 +421,7 @@ public class AccountSetupBasics extends K9Activity
|
|||||||
ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias);
|
ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias);
|
||||||
ServerSettings transportServer = new ServerSettings(SmtpTransport.TRANSPORT_TYPE, "mail." + domain, -1,
|
ServerSettings transportServer = new ServerSettings(SmtpTransport.TRANSPORT_TYPE, "mail." + domain, -1,
|
||||||
ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias);
|
ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias);
|
||||||
String storeUri = Store.createStoreUri(storeServer);
|
String storeUri = RemoteStore.createStoreUri(storeServer);
|
||||||
String transportUri = Transport.createTransportUri(transportServer);
|
String transportUri = Transport.createTransportUri(transportServer);
|
||||||
mAccount.setStoreUri(storeUri);
|
mAccount.setStoreUri(storeUri);
|
||||||
mAccount.setTransportUri(transportUri);
|
mAccount.setTransportUri(transportUri);
|
||||||
|
@ -27,6 +27,7 @@ import com.fsck.k9.mail.Store;
|
|||||||
import com.fsck.k9.mail.Transport;
|
import com.fsck.k9.mail.Transport;
|
||||||
import com.fsck.k9.mail.store.ImapStore;
|
import com.fsck.k9.mail.store.ImapStore;
|
||||||
import com.fsck.k9.mail.store.Pop3Store;
|
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.WebDavStore;
|
||||||
import com.fsck.k9.mail.store.ImapStore.ImapStoreSettings;
|
import com.fsck.k9.mail.store.ImapStore.ImapStoreSettings;
|
||||||
import com.fsck.k9.mail.store.WebDavStore.WebDavStoreSettings;
|
import com.fsck.k9.mail.store.WebDavStore.WebDavStoreSettings;
|
||||||
@ -163,7 +164,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ServerSettings settings = Store.decodeStoreUri(mAccount.getStoreUri());
|
ServerSettings settings = RemoteStore.decodeStoreUri(mAccount.getStoreUri());
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
// The first item is selected if settings.authenticationType is null or is not in mAuthTypeAdapter
|
// 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,
|
ServerSettings settings = new ServerSettings(mStoreType, host, port,
|
||||||
connectionSecurity, authType, username, password, clientCertificateAlias, extra);
|
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_MOBILE, mCompressionMobile.isChecked());
|
||||||
mAccount.setCompression(Account.TYPE_WIFI, mCompressionWifi.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.MessagingException;
|
||||||
import com.fsck.k9.mail.Store;
|
import com.fsck.k9.mail.Store;
|
||||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
import com.fsck.k9.mailstore.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.local.LocalStore;
|
import com.fsck.k9.mailstore.LocalStore;
|
||||||
import com.fsck.k9.service.MailService;
|
import com.fsck.k9.service.MailService;
|
||||||
|
|
||||||
public class FolderSettings extends K9PreferenceActivity {
|
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.fragment.MessageListFragment;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
import com.fsck.k9.mailstore.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.local.LocalMessage;
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
import com.fsck.k9.provider.EmailProvider;
|
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) {
|
synchronized (mHiddenMessageCache) {
|
||||||
for (Message message : messages) {
|
for (LocalMessage message : messages) {
|
||||||
LocalMessage localMessage = (LocalMessage) message;
|
long messageId = message.getId();
|
||||||
long messageId = localMessage.getId();
|
mHiddenMessageCache.put(messageId, message.getFolder().getId());
|
||||||
long folderId = ((LocalFolder) localMessage.getFolder()).getId();
|
|
||||||
mHiddenMessageCache.put(messageId, folderId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ import com.fsck.k9.activity.setup.AccountSetupIncoming;
|
|||||||
import com.fsck.k9.activity.setup.AccountSetupOutgoing;
|
import com.fsck.k9.activity.setup.AccountSetupOutgoing;
|
||||||
import com.fsck.k9.cache.EmailProviderCache;
|
import com.fsck.k9.cache.EmailProviderCache;
|
||||||
import com.fsck.k9.helper.Contacts;
|
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;
|
||||||
import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
|
import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
|
||||||
import com.fsck.k9.mail.Address;
|
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.Pusher;
|
||||||
import com.fsck.k9.mail.Store;
|
import com.fsck.k9.mail.Store;
|
||||||
import com.fsck.k9.mail.Transport;
|
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.MimeMessage;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
import com.fsck.k9.mailstore.MessageRemovalListener;
|
||||||
import com.fsck.k9.mail.store.local.LocalMessage;
|
import com.fsck.k9.mail.MessageRetrievalListener;
|
||||||
import com.fsck.k9.mail.store.local.LocalStore;
|
import com.fsck.k9.mailstore.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.local.LocalStore.PendingCommand;
|
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.Pop3Store;
|
||||||
import com.fsck.k9.mail.store.UnavailableAccountException;
|
import com.fsck.k9.mailstore.UnavailableStorageException;
|
||||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
|
||||||
import com.fsck.k9.provider.EmailProvider;
|
import com.fsck.k9.provider.EmailProvider;
|
||||||
import com.fsck.k9.provider.EmailProvider.StatsColumns;
|
import com.fsck.k9.provider.EmailProvider.StatsColumns;
|
||||||
import com.fsck.k9.search.ConditionsTreeNode;
|
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
|
* Don't modify this list directly, but use {@link addMessage} and
|
||||||
* {@link removeMatchingMessage} instead.
|
* {@link removeMatchingMessage} instead.
|
||||||
*/
|
*/
|
||||||
LinkedList<Message> messages;
|
LinkedList<LocalMessage> messages;
|
||||||
/**
|
/**
|
||||||
* List of references for messages that the user is still to be notified of,
|
* 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
|
* 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) {
|
public NotificationData(int unread) {
|
||||||
unreadBeforeNotification = unread;
|
unreadBeforeNotification = unread;
|
||||||
droppedMessages = new LinkedList<MessageReference>();
|
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.
|
* @param m The new message to add.
|
||||||
*/
|
*/
|
||||||
public void addMessage(Message m) {
|
public void addMessage(LocalMessage m) {
|
||||||
while (messages.size() >= MAX_MESSAGES) {
|
while (messages.size() >= MAX_MESSAGES) {
|
||||||
Message dropped = messages.removeLast();
|
LocalMessage dropped = messages.removeLast();
|
||||||
droppedMessages.addFirst(dropped.makeMessageReference());
|
droppedMessages.addFirst(dropped.makeMessageReference());
|
||||||
}
|
}
|
||||||
messages.addFirst(m);
|
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 (message.makeMessageReference().equals(ref)) {
|
||||||
if (messages.remove(message) && !droppedMessages.isEmpty()) {
|
if (messages.remove(message) && !droppedMessages.isEmpty()) {
|
||||||
Message restoredMessage = droppedMessages.getFirst().restoreToLocalMessage(context);
|
LocalMessage restoredMessage = droppedMessages.getFirst().restoreToLocalMessage(context);
|
||||||
if (restoredMessage != null) {
|
if (restoredMessage != null) {
|
||||||
messages.addLast(restoredMessage);
|
messages.addLast(restoredMessage);
|
||||||
droppedMessages.removeFirst();
|
droppedMessages.removeFirst();
|
||||||
@ -291,7 +294,7 @@ public class MessagingController implements Runnable {
|
|||||||
* List.
|
* List.
|
||||||
*/
|
*/
|
||||||
public void supplyAllMessageRefs(List<MessageReference> refs) {
|
public void supplyAllMessageRefs(List<MessageReference> refs) {
|
||||||
for (Message m : messages) {
|
for (LocalMessage m : messages) {
|
||||||
refs.add(m.makeMessageReference());
|
refs.add(m.makeMessageReference());
|
||||||
}
|
}
|
||||||
refs.addAll(droppedMessages);
|
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 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(),
|
EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(),
|
||||||
mApplication.getApplicationContext());
|
mApplication.getApplicationContext());
|
||||||
cache.hideMessages(messages);
|
cache.hideMessages(messages);
|
||||||
@ -324,13 +327,11 @@ public class MessagingController implements Runnable {
|
|||||||
cache.unhideMessages(messages);
|
cache.unhideMessages(messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMessageSuppressed(Account account, Message message) {
|
private boolean isMessageSuppressed(LocalMessage message) {
|
||||||
LocalMessage localMessage = (LocalMessage) message;
|
long messageId = message.getId();
|
||||||
String accountUuid = account.getUuid();
|
long folderId = message.getFolder().getId();
|
||||||
long messageId = localMessage.getId();
|
|
||||||
long folderId = ((LocalFolder) localMessage.getFolder()).getId();
|
|
||||||
|
|
||||||
EmailProviderCache cache = EmailProviderCache.getCache(accountUuid,
|
EmailProviderCache cache = EmailProviderCache.getCache(message.getFolder().getAccountUuid(),
|
||||||
mApplication.getApplicationContext());
|
mApplication.getApplicationContext());
|
||||||
return cache.isMessageHidden(messageId, folderId);
|
return cache.isMessageHidden(messageId, folderId);
|
||||||
}
|
}
|
||||||
@ -690,15 +691,16 @@ public class MessagingController implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Collecting statistics of the search result
|
// Collecting statistics of the search result
|
||||||
MessageRetrievalListener retrievalListener = new MessageRetrievalListener() {
|
MessageRetrievalListener retrievalListener = new MessageRetrievalListener<LocalMessage>() {
|
||||||
@Override
|
@Override
|
||||||
public void messageStarted(String message, int number, int ofTotal) {}
|
public void messageStarted(String message, int number, int ofTotal) {}
|
||||||
@Override
|
@Override
|
||||||
public void messagesFinished(int number) {}
|
public void messagesFinished(int number) {}
|
||||||
@Override
|
@Override
|
||||||
public void messageFinished(Message message, int number, int ofTotal) {
|
|
||||||
if (!isMessageSuppressed(message.getFolder().getAccount(), message)) {
|
public void messageFinished(LocalMessage message, int number, int ofTotal) {
|
||||||
List<Message> messages = new ArrayList<Message>();
|
if (!isMessageSuppressed(message)) {
|
||||||
|
List<LocalMessage> messages = new ArrayList<LocalMessage>();
|
||||||
|
|
||||||
messages.add(message);
|
messages.add(message);
|
||||||
stats.unreadMessageCount += (!message.isSet(Flag.SEEN)) ? 1 : 0;
|
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);
|
final Account acct = Preferences.getPreferences(mApplication.getApplicationContext()).getAccount(acctUuid);
|
||||||
|
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.remoteSearchStarted(acct, folderName);
|
listener.remoteSearchStarted(folderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Message> extraResults = new ArrayList<Message>();
|
List<Message> extraResults = new ArrayList<Message>();
|
||||||
@ -791,7 +793,7 @@ public class MessagingController implements Runnable {
|
|||||||
messages.clear();
|
messages.clear();
|
||||||
|
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.remoteSearchServerQueryComplete(acct, folderName, remoteMessages.size());
|
listener.remoteSearchServerQueryComplete(folderName, remoteMessages.size(), acct.getRemoteSearchNumResults());
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(remoteMessages, new UidReverseComparator());
|
Collections.sort(remoteMessages, new UidReverseComparator());
|
||||||
@ -811,13 +813,13 @@ public class MessagingController implements Runnable {
|
|||||||
} else {
|
} else {
|
||||||
Log.e(K9.LOG_TAG, "Could not complete remote search", e);
|
Log.e(K9.LOG_TAG, "Could not complete remote search", e);
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.remoteSearchFailed(acct, null, e.getMessage());
|
listener.remoteSearchFailed(null, e.getMessage());
|
||||||
}
|
}
|
||||||
addErrorMessage(acct, null, e);
|
addErrorMessage(acct, null, e);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (listener != null) {
|
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) {
|
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,
|
final LocalFolder localFolder,
|
||||||
List<Message> unsyncedMessages,
|
List<Message> unsyncedMessages,
|
||||||
final List<Message> smallMessages,
|
final List<Message> smallMessages,
|
||||||
@ -1473,9 +1475,9 @@ public class MessagingController implements Runnable {
|
|||||||
final List<Message> chunk = new ArrayList<Message>(UNSYNC_CHUNK_SIZE);
|
final List<Message> chunk = new ArrayList<Message>(UNSYNC_CHUNK_SIZE);
|
||||||
|
|
||||||
remoteFolder.fetch(unsyncedMessages, fp,
|
remoteFolder.fetch(unsyncedMessages, fp,
|
||||||
new MessageRetrievalListener() {
|
new MessageRetrievalListener<T>() {
|
||||||
@Override
|
@Override
|
||||||
public void messageFinished(Message message, int number, int ofTotal) {
|
public void messageFinished(T message, int number, int ofTotal) {
|
||||||
try {
|
try {
|
||||||
String newPushState = remoteFolder.getNewPushState(localFolder.getPushState(), message);
|
String newPushState = remoteFolder.getNewPushState(localFolder.getPushState(), message);
|
||||||
if (newPushState != null) {
|
if (newPushState != null) {
|
||||||
@ -1564,7 +1566,7 @@ public class MessagingController implements Runnable {
|
|||||||
localFolder.appendMessages(messages);
|
localFolder.appendMessages(messages);
|
||||||
|
|
||||||
for (final Message message : messages) {
|
for (final Message message : messages) {
|
||||||
final Message localMessage = localFolder.getMessage(message.getUid());
|
final LocalMessage localMessage = localFolder.getMessage(message.getUid());
|
||||||
syncFlags(localMessage, message);
|
syncFlags(localMessage, message);
|
||||||
if (K9.DEBUG)
|
if (K9.DEBUG)
|
||||||
Log.v(K9.LOG_TAG, "About to notify listeners that we got a new unsynced message "
|
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;
|
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,
|
final LocalFolder localFolder,
|
||||||
List<Message> smallMessages,
|
List<T> smallMessages,
|
||||||
final AtomicInteger progress,
|
final AtomicInteger progress,
|
||||||
final int unreadBeforeStart,
|
final int unreadBeforeStart,
|
||||||
final AtomicInteger newMessages,
|
final AtomicInteger newMessages,
|
||||||
@ -1608,9 +1610,9 @@ public class MessagingController implements Runnable {
|
|||||||
Log.d(K9.LOG_TAG, "SYNC: Fetching small messages for folder " + folder);
|
Log.d(K9.LOG_TAG, "SYNC: Fetching small messages for folder " + folder);
|
||||||
|
|
||||||
remoteFolder.fetch(smallMessages,
|
remoteFolder.fetch(smallMessages,
|
||||||
fp, new MessageRetrievalListener() {
|
fp, new MessageRetrievalListener<T>() {
|
||||||
@Override
|
@Override
|
||||||
public void messageFinished(final Message message, int number, int ofTotal) {
|
public void messageFinished(final T message, int number, int ofTotal) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (!shouldImportMessage(account, folder, message, progress, earliestDate)) {
|
if (!shouldImportMessage(account, folder, message, progress, earliestDate)) {
|
||||||
@ -1620,7 +1622,7 @@ public class MessagingController implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store the updated message locally
|
// Store the updated message locally
|
||||||
final Message localMessage = localFolder.storeSmallMessage(message, new Runnable() {
|
final LocalMessage localMessage = localFolder.storeSmallMessage(message, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
progress.incrementAndGet();
|
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,
|
final LocalFolder localFolder,
|
||||||
List<Message> largeMessages,
|
List<T> largeMessages,
|
||||||
final AtomicInteger progress,
|
final AtomicInteger progress,
|
||||||
final int unreadBeforeStart,
|
final int unreadBeforeStart,
|
||||||
final AtomicInteger newMessages,
|
final AtomicInteger newMessages,
|
||||||
@ -1744,7 +1746,7 @@ public class MessagingController implements Runnable {
|
|||||||
* right now, attachments will be left for later.
|
* 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.
|
* 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
|
// Update the listener with what we've found
|
||||||
progress.incrementAndGet();
|
progress.incrementAndGet();
|
||||||
// TODO do we need to re-fetch this here?
|
// 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
|
// Increment the number of "new messages" if the newly downloaded message is
|
||||||
// not marked as read.
|
// not marked as read.
|
||||||
@ -1821,11 +1823,11 @@ public class MessagingController implements Runnable {
|
|||||||
|
|
||||||
remoteFolder.fetch(undeletedMessages, fp, null);
|
remoteFolder.fetch(undeletedMessages, fp, null);
|
||||||
for (Message remoteMessage : syncFlagMessages) {
|
for (Message remoteMessage : syncFlagMessages) {
|
||||||
Message localMessage = localFolder.getMessage(remoteMessage.getUid());
|
LocalMessage localMessage = localFolder.getMessage(remoteMessage.getUid());
|
||||||
boolean messageChanged = syncFlags(localMessage, remoteMessage);
|
boolean messageChanged = syncFlags(localMessage, remoteMessage);
|
||||||
if (messageChanged) {
|
if (messageChanged) {
|
||||||
boolean shouldBeNotifiedOf = false;
|
boolean shouldBeNotifiedOf = false;
|
||||||
if (localMessage.isSet(Flag.DELETED) || isMessageSuppressed(account, localMessage)) {
|
if (localMessage.isSet(Flag.DELETED) || isMessageSuppressed(localMessage)) {
|
||||||
for (MessagingListener l : getListeners()) {
|
for (MessagingListener l : getListeners()) {
|
||||||
l.synchronizeMailboxRemovedMessage(account, folder, localMessage);
|
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;
|
boolean messageChanged = false;
|
||||||
if (localMessage == null || localMessage.isSet(Flag.DELETED)) {
|
if (localMessage == null || localMessage.isSet(Flag.DELETED)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (remoteMessage.isSet(Flag.DELETED)) {
|
if (remoteMessage.isSet(Flag.DELETED)) {
|
||||||
if (localMessage.getFolder().getAccount().syncRemoteDeletions()) {
|
if (localMessage.getFolder().syncRemoteDeletions()) {
|
||||||
localMessage.setFlag(Flag.DELETED, true);
|
localMessage.setFlag(Flag.DELETED, true);
|
||||||
messageChanged = true;
|
messageChanged = true;
|
||||||
}
|
}
|
||||||
@ -2722,7 +2724,7 @@ public class MessagingController implements Runnable {
|
|||||||
long nowTime = System.currentTimeMillis();
|
long nowTime = System.currentTimeMillis();
|
||||||
Date nowDate = new Date(nowTime);
|
Date nowDate = new Date(nowTime);
|
||||||
message.setInternalDate(nowDate);
|
message.setInternalDate(nowDate);
|
||||||
message.addSentDate(nowDate);
|
message.addSentDate(nowDate, K9.hideTimeZone());
|
||||||
message.setFrom(new Address(account.getEmail(), "K9mail internal"));
|
message.setFrom(new Address(account.getEmail(), "K9mail internal"));
|
||||||
|
|
||||||
localFolder.appendMessages(Collections.singletonList(message));
|
localFolder.appendMessages(Collections.singletonList(message));
|
||||||
@ -2857,7 +2859,7 @@ public class MessagingController implements Runnable {
|
|||||||
* @param newState
|
* @param newState
|
||||||
* {@code true}, if the flag should be set. {@code false} if it should be removed.
|
* {@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) {
|
boolean newState) {
|
||||||
// TODO: Put this into the background, but right now some callers depend on the message
|
// TODO: Put this into the background, but right now some callers depend on the message
|
||||||
// objects being modified right after this method returns.
|
// objects being modified right after this method returns.
|
||||||
@ -3197,7 +3199,7 @@ public class MessagingController implements Runnable {
|
|||||||
try {
|
try {
|
||||||
LocalStore localStore = account.getLocalStore();
|
LocalStore localStore = account.getLocalStore();
|
||||||
|
|
||||||
List<Part> attachments = MimeUtility.collectAttachments(message);
|
List<Part> attachments = MessageExtractor.collectAttachments(message);
|
||||||
for (Part attachment : attachments) {
|
for (Part attachment : attachments) {
|
||||||
attachment.setBody(null);
|
attachment.setBody(null);
|
||||||
}
|
}
|
||||||
@ -3775,7 +3777,7 @@ public class MessagingController implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void moveMessages(final Account account, final String srcFolder,
|
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) {
|
final MessagingListener listener) {
|
||||||
|
|
||||||
suppressMessages(account, messages);
|
suppressMessages(account, messages);
|
||||||
@ -3790,7 +3792,7 @@ public class MessagingController implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void moveMessagesInThread(final Account account, final String srcFolder,
|
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);
|
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) {
|
final String destFolder, final MessagingListener listener) {
|
||||||
|
|
||||||
moveMessages(account, srcFolder, Collections.singletonList(message), destFolder, listener);
|
moveMessages(account, srcFolder, Collections.singletonList(message), destFolder, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void copyMessages(final Account account, final String srcFolder,
|
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) {
|
final MessagingListener listener) {
|
||||||
|
|
||||||
putBackground("copyMessages", null, new Runnable() {
|
putBackground("copyMessages", null, new Runnable() {
|
||||||
@ -3828,7 +3830,7 @@ public class MessagingController implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void copyMessagesInThread(final Account account, final String srcFolder,
|
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() {
|
putBackground("copyMessagesInThread", null, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@ -3851,7 +3853,7 @@ public class MessagingController implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void moveOrCopyMessageSynchronous(final Account account, final String srcFolder,
|
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) {
|
MessagingListener listener) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -3962,7 +3964,7 @@ public class MessagingController implements Runnable {
|
|||||||
localFolder.open(Folder.OPEN_MODE_RW);
|
localFolder.open(Folder.OPEN_MODE_RW);
|
||||||
String uid = localFolder.getMessageUidById(id);
|
String uid = localFolder.getMessageUidById(id);
|
||||||
if (uid != null) {
|
if (uid != null) {
|
||||||
Message message = localFolder.getMessage(uid);
|
LocalMessage message = localFolder.getMessage(uid);
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
deleteMessages(Collections.singletonList(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() {
|
actOnMessages(messages, new MessageActor() {
|
||||||
|
|
||||||
@Override
|
@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 {
|
throws MessagingException {
|
||||||
|
|
||||||
LocalStore localStore = account.getLocalStore();
|
LocalStore localStore = account.getLocalStore();
|
||||||
@ -4025,7 +4027,7 @@ public class MessagingController implements Runnable {
|
|||||||
return messagesInThreads;
|
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() {
|
actOnMessages(messages, new MessageActor() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -4244,13 +4246,12 @@ public class MessagingController implements Runnable {
|
|||||||
try {
|
try {
|
||||||
Intent msg = new Intent(Intent.ACTION_SEND);
|
Intent msg = new Intent(Intent.ACTION_SEND);
|
||||||
String quotedText = null;
|
String quotedText = null;
|
||||||
Part part = MimeUtility.findFirstPartByMimeType(message,
|
Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain");
|
||||||
"text/plain");
|
|
||||||
if (part == null) {
|
if (part == null) {
|
||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||||
}
|
}
|
||||||
if (part != null) {
|
if (part != null) {
|
||||||
quotedText = MimeUtility.getTextFromPart(part);
|
quotedText = MessageExtractor.getTextFromPart(part);
|
||||||
}
|
}
|
||||||
if (quotedText != null) {
|
if (quotedText != null) {
|
||||||
msg.putExtra(Intent.EXTRA_TEXT, quotedText);
|
msg.putExtra(Intent.EXTRA_TEXT, quotedText);
|
||||||
@ -4393,7 +4394,6 @@ public class MessagingController implements Runnable {
|
|||||||
Store localStore = account.getLocalStore();
|
Store localStore = account.getLocalStore();
|
||||||
for (final Folder folder : localStore.getPersonalNamespaces(false)) {
|
for (final Folder folder : localStore.getPersonalNamespaces(false)) {
|
||||||
folder.open(Folder.OPEN_MODE_RW);
|
folder.open(Folder.OPEN_MODE_RW);
|
||||||
folder.refresh(prefs);
|
|
||||||
|
|
||||||
Folder.FolderClass fDisplayClass = folder.getDisplayClass();
|
Folder.FolderClass fDisplayClass = folder.getDisplayClass();
|
||||||
Folder.FolderClass fSyncClass = folder.getSyncClass();
|
Folder.FolderClass fSyncClass = folder.getSyncClass();
|
||||||
@ -4696,7 +4696,7 @@ public class MessagingController implements Runnable {
|
|||||||
if (fromAddrs != null) {
|
if (fromAddrs != null) {
|
||||||
isSelf = account.isAnIdentity(fromAddrs);
|
isSelf = account.isAnIdentity(fromAddrs);
|
||||||
if (!isSelf && fromAddrs.length > 0) {
|
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) {
|
if (rcpts != null && rcpts.length > 0) {
|
||||||
return context.getString(R.string.message_to_fmt,
|
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);
|
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;
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Message findNewestMessageForNotificationLocked(Context context,
|
private LocalMessage findNewestMessageForNotificationLocked(Context context, NotificationData data) {
|
||||||
Account account, NotificationData data) {
|
|
||||||
if (!data.messages.isEmpty()) {
|
if (!data.messages.isEmpty()) {
|
||||||
return data.messages.getFirst();
|
return data.messages.getFirst();
|
||||||
}
|
}
|
||||||
@ -4798,7 +4797,7 @@ public class MessagingController implements Runnable {
|
|||||||
* Creates a notification of a newly received message.
|
* Creates a notification of a newly received message.
|
||||||
*/
|
*/
|
||||||
private void notifyAccount(Context context, Account account,
|
private void notifyAccount(Context context, Account account,
|
||||||
Message message, int previousUnreadMessageCount) {
|
LocalMessage message, int previousUnreadMessageCount) {
|
||||||
final NotificationData data = getNotificationData(account, previousUnreadMessageCount);
|
final NotificationData data = getNotificationData(account, previousUnreadMessageCount);
|
||||||
synchronized (data) {
|
synchronized (data) {
|
||||||
notifyAccountWithDataLocked(context, account, message, 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 static final int NUM_SENDERS_IN_LOCK_SCREEN_NOTIFICATION = 5;
|
||||||
|
|
||||||
private void notifyAccountWithDataLocked(Context context, Account account,
|
private void notifyAccountWithDataLocked(Context context, Account account,
|
||||||
Message message, NotificationData data) {
|
LocalMessage message, NotificationData data) {
|
||||||
boolean updateSilently = false;
|
boolean updateSilently = false;
|
||||||
|
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
/* this can happen if a message we previously notified for is read or deleted remotely */
|
/* 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;
|
updateSilently = true;
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
// seemingly both the message list as well as the overflow list is empty;
|
// seemingly both the message list as well as the overflow list is empty;
|
||||||
@ -5110,7 +5109,7 @@ public class MessagingController implements Runnable {
|
|||||||
int unreadCount,
|
int unreadCount,
|
||||||
CharSequence accountDescription,
|
CharSequence accountDescription,
|
||||||
CharSequence formattedSender,
|
CharSequence formattedSender,
|
||||||
List<Message> messages) {
|
List<? extends Message> messages) {
|
||||||
if (!platformSupportsLockScreenNotifications()) {
|
if (!platformSupportsLockScreenNotifications()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -5307,7 +5306,6 @@ public class MessagingController implements Runnable {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
folder.open(Folder.OPEN_MODE_RW);
|
folder.open(Folder.OPEN_MODE_RW);
|
||||||
folder.refresh(prefs);
|
|
||||||
|
|
||||||
Folder.FolderClass fDisplayClass = folder.getDisplayClass();
|
Folder.FolderClass fDisplayClass = folder.getDisplayClass();
|
||||||
Folder.FolderClass fPushClass = folder.getPushClass();
|
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>>>();
|
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) {
|
if ( message == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Folder folder = message.getFolder();
|
Folder folder = message.getFolder();
|
||||||
Account account = folder.getAccount();
|
Account account = message.getAccount();
|
||||||
|
|
||||||
Map<Folder, List<Message>> folderMap = accountMap.get(account);
|
Map<Folder, List<Message>> folderMap = accountMap.get(account);
|
||||||
if (folderMap == null) {
|
if (folderMap == null) {
|
||||||
|
@ -11,8 +11,8 @@ import com.fsck.k9.mail.Folder;
|
|||||||
|
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.PushReceiver;
|
import com.fsck.k9.mail.PushReceiver;
|
||||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
import com.fsck.k9.mailstore.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.local.LocalStore;
|
import com.fsck.k9.mailstore.LocalStore;
|
||||||
import com.fsck.k9.service.SleepService;
|
import com.fsck.k9.service.SleepService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -11,6 +11,7 @@ import com.fsck.k9.BaseAccount;
|
|||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the interface that {@link MessagingController} will use to callback to requesters.
|
* 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 listLocalMessagesStarted(Account account, String folder) {}
|
||||||
|
|
||||||
public void listLocalMessages(Account account, String folder, Message[] messages) {}
|
|
||||||
|
|
||||||
public void listLocalMessagesAddMessages(Account account, String folder,
|
public void listLocalMessagesAddMessages(Account account, String folder,
|
||||||
List<Message> messages) {}
|
List<LocalMessage> messages) {}
|
||||||
|
|
||||||
public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {}
|
public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {}
|
||||||
|
|
||||||
@ -156,10 +155,9 @@ public class MessagingListener {
|
|||||||
/**
|
/**
|
||||||
* Called when a remote search is started
|
* Called when a remote search is started
|
||||||
*
|
*
|
||||||
* @param acct
|
|
||||||
* @param folder
|
* @param folder
|
||||||
*/
|
*/
|
||||||
public void remoteSearchStarted(Account acct, String folder) {}
|
public void remoteSearchStarted(String folder) {}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -167,35 +165,30 @@ public class MessagingListener {
|
|||||||
*
|
*
|
||||||
* @param numResults
|
* @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
|
* Called when a new result message is available for a remote search
|
||||||
* Can assume headers have been downloaded, but potentially not body.
|
* Can assume headers have been downloaded, but potentially not body.
|
||||||
* @param account
|
|
||||||
* @param folder
|
* @param folder
|
||||||
* @param message
|
* @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
|
* Called when Remote Search is fully complete
|
||||||
*
|
* @param folder
|
||||||
* @param acct
|
|
||||||
* @param folder
|
|
||||||
* @param numResults
|
* @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.
|
* Called when there was a problem with a remote search operation.
|
||||||
*
|
* @param folder
|
||||||
* @param acct
|
|
||||||
* @param folder
|
|
||||||
* @param err
|
* @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
|
* General notification messages subclasses can override to be notified that the controller
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
package com.fsck.k9.mail.store;
|
package com.fsck.k9.controller;
|
||||||
|
|
||||||
import com.fsck.k9.Account;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link Account} is not
|
* An {@link com.fsck.k9.Account} is not
|
||||||
* {@link Account#isAvailable(android.content.Context)}.<br/>
|
* {@link com.fsck.k9.Account#isAvailable(android.content.Context)}.<br/>
|
||||||
* The operation may be retried later.
|
* The operation may be retried later.
|
||||||
*/
|
*/
|
||||||
public class UnavailableAccountException extends RuntimeException {
|
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.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
|
import com.fsck.k9.mail.internet.MessageExtractor;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
|
|
||||||
|
|
||||||
public class CryptoHelper {
|
public class CryptoHelper {
|
||||||
|
|
||||||
public static Pattern PGP_MESSAGE =
|
public static Pattern PGP_MESSAGE =
|
||||||
@ -37,7 +39,7 @@ public class CryptoHelper {
|
|||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||||
}
|
}
|
||||||
if (part != null) {
|
if (part != null) {
|
||||||
data = MimeUtility.getTextFromPart(part);
|
data = MessageExtractor.getTextFromPart(part);
|
||||||
}
|
}
|
||||||
} catch (MessagingException e) {
|
} catch (MessagingException e) {
|
||||||
// guess not...
|
// guess not...
|
||||||
@ -60,7 +62,7 @@ public class CryptoHelper {
|
|||||||
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
part = MimeUtility.findFirstPartByMimeType(message, "text/html");
|
||||||
}
|
}
|
||||||
if (part != null) {
|
if (part != null) {
|
||||||
data = MimeUtility.getTextFromPart(part);
|
data = MessageExtractor.getTextFromPart(part);
|
||||||
}
|
}
|
||||||
} catch (MessagingException e) {
|
} catch (MessagingException e) {
|
||||||
// guess not...
|
// guess not...
|
||||||
|
@ -88,8 +88,9 @@ import com.fsck.k9.mail.Flag;
|
|||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
import com.fsck.k9.mailstore.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.local.LocalStore;
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
|
import com.fsck.k9.mailstore.LocalStore;
|
||||||
import com.fsck.k9.provider.EmailProvider;
|
import com.fsck.k9.provider.EmailProvider;
|
||||||
import com.fsck.k9.provider.EmailProvider.MessageColumns;
|
import com.fsck.k9.provider.EmailProvider.MessageColumns;
|
||||||
import com.fsck.k9.provider.EmailProvider.SpecialColumns;
|
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
|
* 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).
|
* 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 */
|
/* package visibility for faster inner class access */
|
||||||
MessageHelper mMessageHelper;
|
MessageHelper mMessageHelper;
|
||||||
@ -1035,7 +1036,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Folder getFolderById(Account account, long folderId) {
|
private LocalFolder getFolderById(Account account, long folderId) {
|
||||||
try {
|
try {
|
||||||
LocalStore localStore = account.getLocalStore();
|
LocalStore localStore = account.getLocalStore();
|
||||||
LocalFolder localFolder = localStore.getFolderById(folderId);
|
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);
|
mFragmentListener.onReply(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onReplyAll(Message message) {
|
public void onReplyAll(LocalMessage message) {
|
||||||
mFragmentListener.onReplyAll(message);
|
mFragmentListener.onReplyAll(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onForward(Message message) {
|
public void onForward(LocalMessage message) {
|
||||||
mFragmentListener.onForward(message);
|
mFragmentListener.onForward(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onResendMessage(Message message) {
|
public void onResendMessage(LocalMessage message) {
|
||||||
mFragmentListener.onResendMessage(message);
|
mFragmentListener.onResendMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1309,11 +1310,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
changeSort(sorts[curIndex]);
|
changeSort(sorts[curIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDelete(Message message) {
|
private void onDelete(LocalMessage message) {
|
||||||
onDelete(Collections.singletonList(message));
|
onDelete(Collections.singletonList(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDelete(List<Message> messages) {
|
private void onDelete(List<LocalMessage> messages) {
|
||||||
if (K9.confirmDelete()) {
|
if (K9.confirmDelete()) {
|
||||||
// remember the message selection for #onCreateDialog(int)
|
// remember the message selection for #onCreateDialog(int)
|
||||||
mActiveMessages = messages;
|
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) {
|
if (mThreadedList) {
|
||||||
mController.deleteThreads(messages);
|
mController.deleteThreads(messages);
|
||||||
} else {
|
} else {
|
||||||
@ -1345,15 +1346,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER);
|
final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER);
|
||||||
final List<Message> messages = mActiveMessages;
|
final List<LocalMessage> messages = mActiveMessages;
|
||||||
|
|
||||||
if (destFolderName != null) {
|
if (destFolderName != null) {
|
||||||
|
|
||||||
mActiveMessages = null; // don't need it any more
|
mActiveMessages = null; // don't need it any more
|
||||||
|
|
||||||
if (messages.size() > 0) {
|
if (messages.size() > 0) {
|
||||||
Account account = messages.get(0).getFolder().getAccount();
|
messages.get(0).getFolder().setLastSelectedFolderName(destFolderName);
|
||||||
account.setLastSelectedFolderName(destFolderName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
@ -1510,23 +1510,19 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.reply: {
|
case R.id.reply: {
|
||||||
Message message = getMessageAtPosition(adapterPosition);
|
onReply(getMessageAtPosition(adapterPosition));
|
||||||
onReply(message);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.reply_all: {
|
case R.id.reply_all: {
|
||||||
Message message = getMessageAtPosition(adapterPosition);
|
onReplyAll(getMessageAtPosition(adapterPosition));
|
||||||
onReplyAll(message);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.forward: {
|
case R.id.forward: {
|
||||||
Message message = getMessageAtPosition(adapterPosition);
|
onForward(getMessageAtPosition(adapterPosition));
|
||||||
onForward(message);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.send_again: {
|
case R.id.send_again: {
|
||||||
Message message = getMessageAtPosition(adapterPosition);
|
onResendMessage(getMessageAtPosition(adapterPosition));
|
||||||
onResendMessage(message);
|
|
||||||
mSelectedCount = 0;
|
mSelectedCount = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1539,7 +1535,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.delete: {
|
case R.id.delete: {
|
||||||
Message message = getMessageAtPosition(adapterPosition);
|
LocalMessage message = getMessageAtPosition(adapterPosition);
|
||||||
onDelete(message);
|
onDelete(message);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1562,23 +1558,19 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
|
|
||||||
// only if the account supports this
|
// only if the account supports this
|
||||||
case R.id.archive: {
|
case R.id.archive: {
|
||||||
Message message = getMessageAtPosition(adapterPosition);
|
onArchive(getMessageAtPosition(adapterPosition));
|
||||||
onArchive(message);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.spam: {
|
case R.id.spam: {
|
||||||
Message message = getMessageAtPosition(adapterPosition);
|
onSpam(getMessageAtPosition(adapterPosition));
|
||||||
onSpam(message);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.move: {
|
case R.id.move: {
|
||||||
Message message = getMessageAtPosition(adapterPosition);
|
onMove(getMessageAtPosition(adapterPosition));
|
||||||
onMove(message);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.copy: {
|
case R.id.copy: {
|
||||||
Message message = getMessageAtPosition(adapterPosition);
|
onCopy(getMessageAtPosition(adapterPosition));
|
||||||
onCopy(message);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1711,7 +1703,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
|
|
||||||
class MessageListActivityListener extends ActivityListener {
|
class MessageListActivityListener extends ActivityListener {
|
||||||
@Override
|
@Override
|
||||||
public void remoteSearchFailed(Account acct, String folder, final String err) {
|
public void remoteSearchFailed(String folder, final String err) {
|
||||||
mHandler.post(new Runnable() {
|
mHandler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -1725,7 +1717,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remoteSearchStarted(Account acct, String folder) {
|
public void remoteSearchStarted(String folder) {
|
||||||
mHandler.progress(true);
|
mHandler.progress(true);
|
||||||
mHandler.updateFooter(mContext.getString(R.string.remote_search_sending_query));
|
mHandler.updateFooter(mContext.getString(R.string.remote_search_sending_query));
|
||||||
}
|
}
|
||||||
@ -1736,12 +1728,12 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.progress(false);
|
||||||
mHandler.remoteSearchFinished();
|
mHandler.remoteSearchFinished();
|
||||||
mExtraSearchResults = extraResults;
|
mExtraSearchResults = extraResults;
|
||||||
if (extraResults != null && extraResults.size() > 0) {
|
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 {
|
} else {
|
||||||
mHandler.updateFooter("");
|
mHandler.updateFooter("");
|
||||||
}
|
}
|
||||||
@ -1750,11 +1742,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remoteSearchServerQueryComplete(Account account, String folderName, int numResults) {
|
public void remoteSearchServerQueryComplete(String folderName, int numResults, int maxResults) {
|
||||||
mHandler.progress(true);
|
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,
|
mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading_limited,
|
||||||
account.getRemoteSearchNumResults(), numResults));
|
maxResults, numResults));
|
||||||
} else {
|
} else {
|
||||||
mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading, numResults));
|
mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading, numResults));
|
||||||
}
|
}
|
||||||
@ -2416,7 +2408,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
computeBatchDirection();
|
computeBatchDirection();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMove(Message message) {
|
private void onMove(LocalMessage message) {
|
||||||
onMove(Collections.singletonList(message));
|
onMove(Collections.singletonList(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2426,7 +2418,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
* @param messages
|
* @param messages
|
||||||
* Never {@code null}.
|
* Never {@code null}.
|
||||||
*/
|
*/
|
||||||
private void onMove(List<Message> messages) {
|
private void onMove(List<LocalMessage> messages) {
|
||||||
if (!checkCopyOrMovePossible(messages, FolderOperation.MOVE)) {
|
if (!checkCopyOrMovePossible(messages, FolderOperation.MOVE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2440,12 +2432,13 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
folder = null;
|
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));
|
onCopy(Collections.singletonList(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2455,7 +2448,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
* @param messages
|
* @param messages
|
||||||
* Never {@code null}.
|
* Never {@code null}.
|
||||||
*/
|
*/
|
||||||
private void onCopy(List<Message> messages) {
|
private void onCopy(List<LocalMessage> messages) {
|
||||||
if (!checkCopyOrMovePossible(messages, FolderOperation.COPY)) {
|
if (!checkCopyOrMovePossible(messages, FolderOperation.COPY)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2469,7 +2462,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
folder = null;
|
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)
|
* @see #startActivityForResult(Intent, int)
|
||||||
*/
|
*/
|
||||||
private void displayFolderChoice(int requestCode, Account account, Folder folder,
|
private void displayFolderChoice(int requestCode, Folder folder,
|
||||||
List<Message> messages) {
|
String accountUuid, String lastSelectedFolderName,
|
||||||
|
List<LocalMessage> messages) {
|
||||||
|
|
||||||
Intent intent = new Intent(getActivity(), ChooseFolder.class);
|
Intent intent = new Intent(getActivity(), ChooseFolder.class);
|
||||||
intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, account.getUuid());
|
intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, accountUuid);
|
||||||
intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, account.getLastSelectedFolderName());
|
intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, lastSelectedFolderName);
|
||||||
|
|
||||||
if (folder == null) {
|
if (folder == null) {
|
||||||
intent.putExtra(ChooseFolder.EXTRA_SHOW_CURRENT, "yes");
|
intent.putExtra(ChooseFolder.EXTRA_SHOW_CURRENT, "yes");
|
||||||
@ -2504,14 +2501,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
startActivityForResult(intent, requestCode);
|
startActivityForResult(intent, requestCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onArchive(final Message message) {
|
private void onArchive(final LocalMessage message) {
|
||||||
onArchive(Collections.singletonList(message));
|
onArchive(Collections.singletonList(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onArchive(final List<Message> messages) {
|
private void onArchive(final List<LocalMessage> messages) {
|
||||||
Map<Account, List<Message>> messagesByAccount = groupMessagesByAccount(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();
|
Account account = entry.getKey();
|
||||||
String archiveFolder = account.getArchiveFolderName();
|
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) {
|
private Map<Account, List<LocalMessage>> groupMessagesByAccount(final List<LocalMessage> messages) {
|
||||||
Map<Account, List<Message>> messagesByAccount = new HashMap<Account, List<Message>>();
|
Map<Account, List<LocalMessage>> messagesByAccount = new HashMap<Account, List<LocalMessage>>();
|
||||||
for (Message message : messages) {
|
for (LocalMessage message : messages) {
|
||||||
Account account = message.getFolder().getAccount();
|
Account account = message.getAccount();
|
||||||
|
|
||||||
List<Message> msgList = messagesByAccount.get(account);
|
List<LocalMessage> msgList = messagesByAccount.get(account);
|
||||||
if (msgList == null) {
|
if (msgList == null) {
|
||||||
msgList = new ArrayList<Message>();
|
msgList = new ArrayList<LocalMessage>();
|
||||||
messagesByAccount.put(account, msgList);
|
messagesByAccount.put(account, msgList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2537,7 +2534,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
return messagesByAccount;
|
return messagesByAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSpam(Message message) {
|
private void onSpam(LocalMessage message) {
|
||||||
onSpam(Collections.singletonList(message));
|
onSpam(Collections.singletonList(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2547,7 +2544,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
* @param messages
|
* @param messages
|
||||||
* The messages to move to the spam folder. Never {@code null}.
|
* 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()) {
|
if (K9.confirmSpam()) {
|
||||||
// remember the message selection for #onCreateDialog(int)
|
// remember the message selection for #onCreateDialog(int)
|
||||||
mActiveMessages = messages;
|
mActiveMessages = messages;
|
||||||
@ -2557,10 +2554,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSpamConfirmed(List<Message> messages) {
|
private void onSpamConfirmed(List<LocalMessage> messages) {
|
||||||
Map<Account, List<Message>> messagesByAccount = groupMessagesByAccount(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();
|
Account account = entry.getKey();
|
||||||
String spamFolder = account.getSpamFolderName();
|
String spamFolder = account.getSpamFolderName();
|
||||||
|
|
||||||
@ -2584,7 +2581,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
*
|
*
|
||||||
* @return {@code true}, if operation is possible.
|
* @return {@code true}, if operation is possible.
|
||||||
*/
|
*/
|
||||||
private boolean checkCopyOrMovePossible(final List<Message> messages,
|
private boolean checkCopyOrMovePossible(final List<LocalMessage> messages,
|
||||||
final FolderOperation operation) {
|
final FolderOperation operation) {
|
||||||
|
|
||||||
if (messages.isEmpty()) {
|
if (messages.isEmpty()) {
|
||||||
@ -2592,11 +2589,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
for (final Message message : messages) {
|
for (final LocalMessage message : messages) {
|
||||||
if (first) {
|
if (first) {
|
||||||
first = false;
|
first = false;
|
||||||
// account check
|
// account check
|
||||||
final Account account = message.getFolder().getAccount();
|
final Account account = message.getAccount();
|
||||||
if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(account)) ||
|
if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(account)) ||
|
||||||
(operation == FolderOperation.COPY && !mController.isCopyCapable(account))) {
|
(operation == FolderOperation.COPY && !mController.isCopyCapable(account))) {
|
||||||
return false;
|
return false;
|
||||||
@ -2622,7 +2619,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
* @param destination
|
* @param destination
|
||||||
* The name of the destination folder. Never {@code null}.
|
* 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);
|
copyOrMove(messages, destination, FolderOperation.COPY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2634,7 +2631,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
* @param destination
|
* @param destination
|
||||||
* The name of the destination folder. Never {@code null}.
|
* 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);
|
copyOrMove(messages, destination, FolderOperation.MOVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2650,12 +2647,12 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
* @param operation
|
* @param operation
|
||||||
* Specifies what operation to perform. Never {@code null}.
|
* 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) {
|
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)) ||
|
if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(message)) ||
|
||||||
(operation == FolderOperation.COPY && !mController.isCopyCapable(message))) {
|
(operation == FolderOperation.COPY && !mController.isCopyCapable(message))) {
|
||||||
|
|
||||||
@ -2674,19 +2671,19 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Message> outMessages = folderMap.get(folderName);
|
List<LocalMessage> outMessages = folderMap.get(folderName);
|
||||||
if (outMessages == null) {
|
if (outMessages == null) {
|
||||||
outMessages = new ArrayList<Message>();
|
outMessages = new ArrayList<LocalMessage>();
|
||||||
folderMap.put(folderName, outMessages);
|
folderMap.put(folderName, outMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
outMessages.add(message);
|
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();
|
String folderName = entry.getKey();
|
||||||
List<Message> outMessages = entry.getValue();
|
List<LocalMessage> outMessages = entry.getValue();
|
||||||
Account account = outMessages.get(0).getFolder().getAccount();
|
Account account = outMessages.get(0).getAccount();
|
||||||
|
|
||||||
if (operation == FolderOperation.MOVE) {
|
if (operation == FolderOperation.MOVE) {
|
||||||
if (mThreadedList) {
|
if (mThreadedList) {
|
||||||
@ -2859,7 +2856,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
*/
|
*/
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.delete: {
|
case R.id.delete: {
|
||||||
List<Message> messages = getCheckedMessages();
|
List<LocalMessage> messages = getCheckedMessages();
|
||||||
onDelete(messages);
|
onDelete(messages);
|
||||||
mSelectedCount = 0;
|
mSelectedCount = 0;
|
||||||
break;
|
break;
|
||||||
@ -2887,26 +2884,22 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
|
|
||||||
// only if the account supports this
|
// only if the account supports this
|
||||||
case R.id.archive: {
|
case R.id.archive: {
|
||||||
List<Message> messages = getCheckedMessages();
|
onArchive(getCheckedMessages());
|
||||||
onArchive(messages);
|
|
||||||
mSelectedCount = 0;
|
mSelectedCount = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.spam: {
|
case R.id.spam: {
|
||||||
List<Message> messages = getCheckedMessages();
|
onSpam(getCheckedMessages());
|
||||||
onSpam(messages);
|
|
||||||
mSelectedCount = 0;
|
mSelectedCount = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.move: {
|
case R.id.move: {
|
||||||
List<Message> messages = getCheckedMessages();
|
onMove(getCheckedMessages());
|
||||||
onMove(messages);
|
|
||||||
mSelectedCount = 0;
|
mSelectedCount = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case R.id.copy: {
|
case R.id.copy: {
|
||||||
List<Message> messages = getCheckedMessages();
|
onCopy(getCheckedMessages());
|
||||||
onCopy(messages);
|
|
||||||
mSelectedCount = 0;
|
mSelectedCount = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2987,7 +2980,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
final Folder remoteFolder = mCurrentFolder.folder;
|
final Folder remoteFolder = mCurrentFolder.folder;
|
||||||
remoteFolder.close();
|
remoteFolder.close();
|
||||||
// Send a remoteSearchFinished() message for good measure.
|
// 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) {
|
} catch (Exception e) {
|
||||||
// Since the user is going back, log and squash any exceptions.
|
// 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);
|
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 setMessageListProgress(int level);
|
||||||
void showThread(Account account, String folderName, long rootId);
|
void showThread(Account account, String folderName, long rootId);
|
||||||
void showMoreFromSameSender(String senderAddress);
|
void showMoreFromSameSender(String senderAddress);
|
||||||
void onResendMessage(Message message);
|
void onResendMessage(LocalMessage message);
|
||||||
void onForward(Message message);
|
void onForward(LocalMessage message);
|
||||||
void onReply(Message message);
|
void onReply(LocalMessage message);
|
||||||
void onReplyAll(Message message);
|
void onReplyAll(LocalMessage message);
|
||||||
void openMessage(MessageReference messageReference);
|
void openMessage(MessageReference messageReference);
|
||||||
void setMessageListTitle(String title);
|
void setMessageListTitle(String title);
|
||||||
void setMessageListSubTitle(String subTitle);
|
void setMessageListSubTitle(String subTitle);
|
||||||
@ -3135,7 +3128,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
changeSort(mSortType);
|
changeSort(mSortType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Message getSelectedMessage() {
|
private LocalMessage getSelectedMessage() {
|
||||||
int listViewPosition = mListView.getSelectedItemPosition();
|
int listViewPosition = mListView.getSelectedItemPosition();
|
||||||
int adapterPosition = listViewToAdapterPosition(listViewPosition);
|
int adapterPosition = listViewToAdapterPosition(listViewPosition);
|
||||||
|
|
||||||
@ -3158,7 +3151,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
return AdapterView.INVALID_POSITION;
|
return AdapterView.INVALID_POSITION;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Message getMessageAtPosition(int adapterPosition) {
|
private LocalMessage getMessageAtPosition(int adapterPosition) {
|
||||||
if (adapterPosition == AdapterView.INVALID_POSITION) {
|
if (adapterPosition == AdapterView.INVALID_POSITION) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -3168,7 +3161,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
|
|
||||||
Account account = getAccountFromCursor(cursor);
|
Account account = getAccountFromCursor(cursor);
|
||||||
long folderId = cursor.getLong(FOLDER_ID_COLUMN);
|
long folderId = cursor.getLong(FOLDER_ID_COLUMN);
|
||||||
Folder folder = getFolderById(account, folderId);
|
LocalFolder folder = getFolderById(account, folderId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return folder.getMessage(uid);
|
return folder.getMessage(uid);
|
||||||
@ -3179,14 +3172,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Message> getCheckedMessages() {
|
private List<LocalMessage> getCheckedMessages() {
|
||||||
List<Message> messages = new ArrayList<Message>(mSelected.size());
|
List<LocalMessage> messages = new ArrayList<LocalMessage>(mSelected.size());
|
||||||
for (int position = 0, end = mAdapter.getCount(); position < end; position++) {
|
for (int position = 0, end = mAdapter.getCount(); position < end; position++) {
|
||||||
Cursor cursor = (Cursor) mAdapter.getItem(position);
|
Cursor cursor = (Cursor) mAdapter.getItem(position);
|
||||||
long uniqueId = cursor.getLong(mUniqueIdColumn);
|
long uniqueId = cursor.getLong(mUniqueIdColumn);
|
||||||
|
|
||||||
if (mSelected.contains(uniqueId)) {
|
if (mSelected.contains(uniqueId)) {
|
||||||
Message message = getMessageAtPosition(position);
|
LocalMessage message = getMessageAtPosition(position);
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
messages.add(message);
|
messages.add(message);
|
||||||
}
|
}
|
||||||
@ -3197,7 +3190,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onDelete() {
|
public void onDelete() {
|
||||||
Message message = getSelectedMessage();
|
LocalMessage message = getSelectedMessage();
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
onDelete(Collections.singletonList(message));
|
onDelete(Collections.singletonList(message));
|
||||||
}
|
}
|
||||||
@ -3227,21 +3220,21 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onMove() {
|
public void onMove() {
|
||||||
Message message = getSelectedMessage();
|
LocalMessage message = getSelectedMessage();
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
onMove(message);
|
onMove(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onArchive() {
|
public void onArchive() {
|
||||||
Message message = getSelectedMessage();
|
LocalMessage message = getSelectedMessage();
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
onArchive(message);
|
onArchive(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCopy() {
|
public void onCopy() {
|
||||||
Message message = getSelectedMessage();
|
LocalMessage message = getSelectedMessage();
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
onCopy(message);
|
onCopy(message);
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ import com.fsck.k9.mail.Flag;
|
|||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Part;
|
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;
|
||||||
import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback;
|
import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback;
|
||||||
import com.fsck.k9.view.MessageHeader;
|
import com.fsck.k9.view.MessageHeader;
|
||||||
@ -75,7 +75,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
|
|||||||
private PgpData mPgpData;
|
private PgpData mPgpData;
|
||||||
private Account mAccount;
|
private Account mAccount;
|
||||||
private MessageReference mMessageReference;
|
private MessageReference mMessageReference;
|
||||||
private Message mMessage;
|
private LocalMessage mMessage;
|
||||||
private MessagingController mController;
|
private MessagingController mController;
|
||||||
private Listener mListener = new Listener();
|
private Listener mListener = new Listener();
|
||||||
private MessageViewHandler mHandler = new MessageViewHandler();
|
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
|
// Disable the delete button after it's tapped (to try to prevent
|
||||||
// accidental clicks)
|
// accidental clicks)
|
||||||
mFragmentListener.disableDeleteAction();
|
mFragmentListener.disableDeleteAction();
|
||||||
Message messageToDelete = mMessage;
|
LocalMessage messageToDelete = mMessage;
|
||||||
mFragmentListener.showNextMessageOrReturn();
|
mFragmentListener.showNextMessageOrReturn();
|
||||||
mController.deleteMessages(Collections.singletonList(messageToDelete), null);
|
mController.deleteMessages(Collections.singletonList(messageToDelete), null);
|
||||||
}
|
}
|
||||||
@ -341,7 +341,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
|
|||||||
|
|
||||||
private void refileMessage(String dstFolder) {
|
private void refileMessage(String dstFolder) {
|
||||||
String srcFolder = mMessageReference.folderName;
|
String srcFolder = mMessageReference.folderName;
|
||||||
Message messageToMove = mMessage;
|
LocalMessage messageToMove = mMessage;
|
||||||
mFragmentListener.showNextMessageOrReturn();
|
mFragmentListener.showNextMessageOrReturn();
|
||||||
mController.moveMessage(mAccount, srcFolder, messageToMove, dstFolder, null);
|
mController.moveMessage(mAccount, srcFolder, messageToMove, dstFolder, null);
|
||||||
}
|
}
|
||||||
@ -576,7 +576,8 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
|
|||||||
@Override
|
@Override
|
||||||
public void loadMessageForViewBodyAvailable(final Account account, String folder,
|
public void loadMessageForViewBodyAvailable(final Account account, String folder,
|
||||||
String uid, final Message message) {
|
String uid, final Message message) {
|
||||||
if (!mMessageReference.uid.equals(uid) ||
|
if (!(message instanceof LocalMessage) ||
|
||||||
|
!mMessageReference.uid.equals(uid) ||
|
||||||
!mMessageReference.folderName.equals(folder) ||
|
!mMessageReference.folderName.equals(folder) ||
|
||||||
!mMessageReference.accountUuid.equals(account.getUuid())) {
|
!mMessageReference.accountUuid.equals(account.getUuid())) {
|
||||||
return;
|
return;
|
||||||
@ -586,7 +587,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
mMessage = message;
|
mMessage = (LocalMessage) message;
|
||||||
mMessageView.setMessage(account, (LocalMessage) message, mPgpData,
|
mMessageView.setMessage(account, (LocalMessage) message, mPgpData,
|
||||||
mController, mListener);
|
mController, mListener);
|
||||||
mFragmentListener.updateMenu();
|
mFragmentListener.updateMenu();
|
||||||
@ -845,10 +846,10 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public interface MessageViewFragmentListener {
|
public interface MessageViewFragmentListener {
|
||||||
public void onForward(Message mMessage, PgpData mPgpData);
|
public void onForward(LocalMessage mMessage, PgpData mPgpData);
|
||||||
public void disableDeleteAction();
|
public void disableDeleteAction();
|
||||||
public void onReplyAll(Message mMessage, PgpData mPgpData);
|
public void onReplyAll(LocalMessage mMessage, PgpData mPgpData);
|
||||||
public void onReply(Message mMessage, PgpData mPgpData);
|
public void onReply(LocalMessage mMessage, PgpData mPgpData);
|
||||||
public void displayMessageSubject(String title);
|
public void displayMessageSubject(String title);
|
||||||
public void setProgress(boolean b);
|
public void setProgress(boolean b);
|
||||||
public void showNextMessageOrReturn();
|
public void showNextMessageOrReturn();
|
||||||
|
@ -4,6 +4,7 @@ import android.text.*;
|
|||||||
import android.text.Html.TagHandler;
|
import android.text.Html.TagHandler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
|
|
||||||
import org.xml.sax.XMLReader;
|
import org.xml.sax.XMLReader;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -13,11 +14,75 @@ import java.util.HashSet;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains common routines to convert html to text and vice versa.
|
* Contains common routines to convert html to text and vice versa.
|
||||||
*/
|
*/
|
||||||
public class HtmlConverter {
|
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
|
* 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
|
* 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.
|
* @param outputBuffer Buffer to append linked text to.
|
||||||
*/
|
*/
|
||||||
protected static void linkifyText(final String text, final StringBuffer outputBuffer) {
|
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()) {
|
while (m.find()) {
|
||||||
int start = m.start();
|
int start = m.start();
|
||||||
if (start == 0 || (start != 0 && prepared.charAt(start - 1) != '@')) {
|
if (start == 0 || (start != 0 && prepared.charAt(start - 1) != '@')) {
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package com.fsck.k9.helper;
|
package com.fsck.k9.helper;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.Account;
|
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.activity.MessageInfoHolder;
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
import com.fsck.k9.mail.Message;
|
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Message.RecipientType;
|
import com.fsck.k9.mail.Message.RecipientType;
|
||||||
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
|
|
||||||
public class MessageHelper {
|
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;
|
private static MessageHelper sInstance;
|
||||||
|
|
||||||
@ -32,8 +48,10 @@ public class MessageHelper {
|
|||||||
mContext = context;
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void populate(final MessageInfoHolder target, final Message message,
|
public void populate(final MessageInfoHolder target,
|
||||||
final FolderInfoHolder folder, final Account account) {
|
final LocalMessage message,
|
||||||
|
final FolderInfoHolder folder,
|
||||||
|
Account account) {
|
||||||
final Contacts contactHelper = K9.showContactName() ? Contacts.getInstance(mContext) : null;
|
final Contacts contactHelper = K9.showContactName() ? Contacts.getInstance(mContext) : null;
|
||||||
try {
|
try {
|
||||||
target.message = message;
|
target.message = message;
|
||||||
@ -53,11 +71,11 @@ public class MessageHelper {
|
|||||||
Address[] addrs = message.getFrom();
|
Address[] addrs = message.getFrom();
|
||||||
|
|
||||||
if (addrs.length > 0 && account.isAnIdentity(addrs[0])) {
|
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.compareCounterparty = to.toString();
|
||||||
target.sender = new SpannableStringBuilder(mContext.getString(R.string.message_to_label)).append(to);
|
target.sender = new SpannableStringBuilder(mContext.getString(R.string.message_to_label)).append(to);
|
||||||
} else {
|
} else {
|
||||||
target.sender = Address.toFriendly(addrs, contactHelper);
|
target.sender = toFriendly(addrs, contactHelper);
|
||||||
target.compareCounterparty = target.sender.toString();
|
target.compareCounterparty = target.sender.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,14 +86,9 @@ public class MessageHelper {
|
|||||||
target.senderAddress = target.compareCounterparty;
|
target.senderAddress = target.compareCounterparty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
target.uid = message.getUid();
|
target.uid = message.getUid();
|
||||||
|
target.account = message.getFolder().getAccountUuid();
|
||||||
target.account = account.getUuid();
|
target.uri = message.getUri();
|
||||||
target.uri = "email://messages/" + account.getAccountNumber() + "/" + message.getFolder().getName() + "/" + message.getUid();
|
|
||||||
|
|
||||||
} catch (MessagingException me) {
|
} catch (MessagingException me) {
|
||||||
Log.w(K9.LOG_TAG, "Unable to load message info", me);
|
Log.w(K9.LOG_TAG, "Unable to load message info", me);
|
||||||
}
|
}
|
||||||
@ -86,11 +99,11 @@ public class MessageHelper {
|
|||||||
|
|
||||||
CharSequence displayName;
|
CharSequence displayName;
|
||||||
if (fromAddrs.length > 0 && account.isAnIdentity(fromAddrs[0])) {
|
if (fromAddrs.length > 0 && account.isAnIdentity(fromAddrs[0])) {
|
||||||
CharSequence to = Address.toFriendly(toAddrs, contactHelper);
|
CharSequence to = toFriendly(toAddrs, contactHelper);
|
||||||
displayName = new SpannableStringBuilder(
|
displayName = new SpannableStringBuilder(
|
||||||
mContext.getString(R.string.message_to_label)).append(to);
|
mContext.getString(R.string.message_to_label)).append(to);
|
||||||
} else {
|
} else {
|
||||||
displayName = Address.toFriendly(fromAddrs, contactHelper);
|
displayName = toFriendly(fromAddrs, contactHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
return displayName;
|
return displayName;
|
||||||
@ -104,4 +117,69 @@ public class MessageHelper {
|
|||||||
}
|
}
|
||||||
return false;
|
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 android.widget.TextView;
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.mail.filter.Base64;
|
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -91,22 +90,6 @@ public class Utility {
|
|||||||
return TextUtils.join(String.valueOf(separator), parts);
|
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) {
|
public static boolean requiredFieldValid(TextView view) {
|
||||||
return view.getText() != null && view.getText().length() > 0;
|
return view.getText() != null && view.getText().length() > 0;
|
||||||
}
|
}
|
||||||
@ -130,48 +113,6 @@ public class Utility {
|
|||||||
return false;
|
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
|
* 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
|
* 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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.apache.james.mime4j.MimeException;
|
import org.apache.james.mime4j.MimeException;
|
||||||
import org.apache.james.mime4j.codec.EncoderUtil;
|
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.dom.address.MailboxList;
|
||||||
import org.apache.james.mime4j.field.address.AddressBuilder;
|
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.TextUtils;
|
||||||
import android.text.style.ForegroundColorSpan;
|
|
||||||
import android.text.util.Rfc822Token;
|
import android.text.util.Rfc822Token;
|
||||||
import android.text.util.Rfc822Tokenizer;
|
import android.text.util.Rfc822Tokenizer;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
|
||||||
import com.fsck.k9.helper.Contacts;
|
|
||||||
import com.fsck.k9.helper.Utility;
|
|
||||||
|
|
||||||
|
|
||||||
public class Address {
|
public class Address {
|
||||||
/**
|
private static final Pattern ATOM = Pattern.compile("^(?:[a-zA-Z0-9!#$%&'*+\\-/=?^_`{|}~]|\\s)+$");
|
||||||
* If the number of addresses exceeds this value the addresses aren't
|
|
||||||
* resolved to the names of Android contacts.
|
|
||||||
*
|
|
||||||
* <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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutable empty {@link Address} array
|
* Immutable empty {@link Address} array
|
||||||
@ -162,12 +145,12 @@ public class Address {
|
|||||||
Mailbox mailbox = (Mailbox)address;
|
Mailbox mailbox = (Mailbox)address;
|
||||||
addresses.add(new Address(mailbox.getLocalPart() + "@" + mailbox.getDomain(), mailbox.getName(), false));
|
addresses.add(new Address(mailbox.getLocalPart() + "@" + mailbox.getDomain(), mailbox.getName(), false));
|
||||||
} else {
|
} else {
|
||||||
Log.e(K9.LOG_TAG, "Unknown address type from Mime4J: "
|
Log.e(LOG_TAG, "Unknown address type from Mime4J: "
|
||||||
+ address.getClass().toString());
|
+ address.getClass().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (MimeException pe) {
|
} 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
|
//but we do an silent failover : we just use the given string as name with empty address
|
||||||
addresses.add(new Address(null, addressList, false));
|
addresses.add(new Address(null, addressList, false));
|
||||||
}
|
}
|
||||||
@ -198,7 +181,7 @@ public class Address {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (!TextUtils.isEmpty(mPersonal)) {
|
if (!TextUtils.isEmpty(mPersonal)) {
|
||||||
return Utility.quoteAtoms(mPersonal) + " <" + mAddress + ">";
|
return quoteAtoms(mPersonal) + " <" + mAddress + ">";
|
||||||
} else {
|
} else {
|
||||||
return mAddress;
|
return mAddress;
|
||||||
}
|
}
|
||||||
@ -233,77 +216,6 @@ public class Address {
|
|||||||
return sb.toString();
|
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()
|
* Unpacks an address list previously packed with packAddressList()
|
||||||
@ -368,4 +280,44 @@ public class Address {
|
|||||||
}
|
}
|
||||||
return sb.toString();
|
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.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
|
||||||
|
|
||||||
public interface Body {
|
public interface Body {
|
||||||
/**
|
/**
|
||||||
* Returns the raw data of the body, without transfer encoding etc applied.
|
* 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).
|
* 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}.
|
* Writes the body's data to the given {@link OutputStream}.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
package com.fsck.k9.mail;
|
package com.fsck.k9.mail;
|
||||||
|
|
||||||
|
|
||||||
public abstract class BodyPart implements Part {
|
public abstract class BodyPart implements Part {
|
||||||
private Multipart mParent;
|
private Multipart mParent;
|
||||||
|
|
||||||
@ -13,7 +13,4 @@ public abstract class BodyPart implements Part {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract void setEncoding(String encoding) throws MessagingException;
|
public abstract void setEncoding(String encoding) throws MessagingException;
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract void setUsing7bitTransport() throws MessagingException;
|
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,15 @@
|
|||||||
package com.fsck.k9.mail;
|
package com.fsck.k9.mail;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import android.util.Log;
|
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 {
|
public abstract class Folder<T extends Message> {
|
||||||
protected final Account mAccount;
|
|
||||||
|
|
||||||
private String status = null;
|
private String status = null;
|
||||||
private long lastChecked = 0;
|
private long lastChecked = 0;
|
||||||
private long lastPush = 0;
|
private long lastPush = 0;
|
||||||
@ -32,10 +26,6 @@ public abstract class Folder {
|
|||||||
HOLDS_FOLDERS, HOLDS_MESSAGES,
|
HOLDS_FOLDERS, HOLDS_MESSAGES,
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Folder(Account account) {
|
|
||||||
mAccount = account;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forces an open of the MailProvider. If the provider is already open this
|
* Forces an open of the MailProvider. If the provider is already open this
|
||||||
* function returns without doing anything.
|
* function returns without doing anything.
|
||||||
@ -83,7 +73,7 @@ public abstract class Folder {
|
|||||||
public abstract int getUnreadMessageCount() throws MessagingException;
|
public abstract int getUnreadMessageCount() throws MessagingException;
|
||||||
public abstract int getFlaggedMessageCount() 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.
|
* 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
|
* @return List of messages
|
||||||
* @throws MessagingException
|
* @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
|
* 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.
|
* @param listener Listener to notify as we download messages.
|
||||||
* @return List of 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);
|
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;
|
throws MessagingException;
|
||||||
|
|
||||||
public abstract Map<String, String> appendMessages(List<? extends Message> messages) throws MessagingException;
|
public abstract Map<String, String> appendMessages(List<? extends Message> messages) throws MessagingException;
|
||||||
@ -149,15 +139,15 @@ public abstract class Folder {
|
|||||||
* @throws MessagingException
|
* @throws MessagingException
|
||||||
*/
|
*/
|
||||||
public abstract void fetch(List<? extends Message> messages, FetchProfile fp,
|
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,
|
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
|
// This is causing trouble. Disabled for now. See issue 1733
|
||||||
//throw new RuntimeException("fetchPart() not implemented.");
|
//throw new RuntimeException("fetchPart() not implemented.");
|
||||||
|
|
||||||
if (K9.DEBUG)
|
if (K9MailLib.isDebug())
|
||||||
Log.d(K9.LOG_TAG, "fetchPart() not implemented.");
|
Log.d(LOG_TAG, "fetchPart() not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void delete(boolean recurse) throws MessagingException;
|
public abstract void delete(boolean recurse) throws MessagingException;
|
||||||
@ -225,10 +215,6 @@ public abstract class Folder {
|
|||||||
return getSyncClass();
|
return getSyncClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refresh(Preferences preferences) throws MessagingException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isInTopGroup() {
|
public boolean isInTopGroup() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -241,10 +227,6 @@ public abstract class Folder {
|
|||||||
this.status = status;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getAccount() {
|
|
||||||
return mAccount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Message> search(String queryString, final Set<Flag> requiredFlags, final Set<Flag> forbiddenFlags)
|
public List<Message> search(String queryString, final Set<Flag> requiredFlags, final Set<Flag> forbiddenFlags)
|
||||||
throws MessagingException {
|
throws MessagingException {
|
||||||
throw new MessagingException("K-9 does not support searches on this folder type");
|
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;
|
package com.fsck.k9.mail;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import android.util.Log;
|
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.CountingOutputStream;
|
||||||
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
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 {
|
public abstract class Message implements Part, CompositeBody {
|
||||||
|
|
||||||
private MessageReference mReference = null;
|
|
||||||
|
|
||||||
public enum RecipientType {
|
public enum RecipientType {
|
||||||
TO, CC, BCC,
|
TO, CC, BCC,
|
||||||
}
|
}
|
||||||
@ -54,9 +48,8 @@ public abstract class Message implements Part, CompositeBody {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Message other = (Message)o;
|
Message other = (Message)o;
|
||||||
return (mUid.equals(other.getUid())
|
return (getUid().equals(other.getUid())
|
||||||
&& mFolder.getName().equals(other.getFolder().getName())
|
&& getFolder().getName().equals(other.getFolder().getName()));
|
||||||
&& mFolder.getAccount().getUuid().equals(other.getFolder().getAccount().getUuid()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -65,7 +58,6 @@ public abstract class Message implements Part, CompositeBody {
|
|||||||
|
|
||||||
int result = 1;
|
int result = 1;
|
||||||
result = MULTIPLIER * result + mFolder.getName().hashCode();
|
result = MULTIPLIER * result + mFolder.getName().hashCode();
|
||||||
result = MULTIPLIER * result + mFolder.getAccount().getUuid().hashCode();
|
|
||||||
result = MULTIPLIER * result + mUid.hashCode();
|
result = MULTIPLIER * result + mUid.hashCode();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -75,7 +67,6 @@ public abstract class Message implements Part, CompositeBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setUid(String uid) {
|
public void setUid(String uid) {
|
||||||
mReference = null;
|
|
||||||
this.mUid = uid;
|
this.mUid = uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +88,7 @@ public abstract class Message implements Part, CompositeBody {
|
|||||||
|
|
||||||
public abstract Date getSentDate();
|
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;
|
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;
|
public abstract void setReferences(String references) throws MessagingException;
|
||||||
|
|
||||||
@Override
|
public abstract Set<String> getHeaderNames() throws MessagingException;
|
||||||
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 long getId();
|
public abstract long getId();
|
||||||
|
|
||||||
@ -249,20 +219,10 @@ public abstract class Message implements Part, CompositeBody {
|
|||||||
public void destroy() throws MessagingException {}
|
public void destroy() throws MessagingException {}
|
||||||
|
|
||||||
@Override
|
@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 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() {
|
public long calculateSize() {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@ -272,9 +232,9 @@ public abstract class Message implements Part, CompositeBody {
|
|||||||
eolOut.flush();
|
eolOut.flush();
|
||||||
return out.getCount();
|
return out.getCount();
|
||||||
} catch (IOException e) {
|
} 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) {
|
} 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;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -282,14 +242,12 @@ public abstract class Message implements Part, CompositeBody {
|
|||||||
/**
|
/**
|
||||||
* Copy the contents of this object into another {@code Message} object.
|
* Copy the contents of this object into another {@code Message} object.
|
||||||
*
|
*
|
||||||
* @param destination
|
* @param destination The {@code Message} object to receive the contents of this instance.
|
||||||
* The {@code Message} object to receive the contents of this instance.
|
|
||||||
*/
|
*/
|
||||||
protected void copy(Message destination) {
|
protected void copy(Message destination) {
|
||||||
destination.mUid = mUid;
|
destination.mUid = mUid;
|
||||||
destination.mInternalDate = mInternalDate;
|
destination.mInternalDate = mInternalDate;
|
||||||
destination.mFolder = mFolder;
|
destination.mFolder = mFolder;
|
||||||
destination.mReference = mReference;
|
|
||||||
|
|
||||||
// mFlags contents can change during the object lifetime, so copy the Set
|
// mFlags contents can change during the object lifetime, so copy the Set
|
||||||
destination.mFlags = EnumSet.copyOf(mFlags);
|
destination.mFlags = EnumSet.copyOf(mFlags);
|
||||||
@ -308,6 +266,5 @@ public abstract class Message implements Part, CompositeBody {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public abstract Message clone();
|
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 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>
|
* 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 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;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
|
||||||
public abstract class Multipart implements CompositeBody {
|
public abstract class Multipart implements CompositeBody {
|
||||||
@ -64,7 +64,7 @@ public abstract class Multipart implements CompositeBody {
|
|||||||
BodyPart part = mParts.get(0);
|
BodyPart part = mParts.get(0);
|
||||||
Body body = part.getBody();
|
Body body = part.getBody();
|
||||||
if (body instanceof TextBody) {
|
if (body instanceof TextBody) {
|
||||||
MimeUtility.setCharset(charset, part);
|
CharsetSupport.setCharset(charset, part);
|
||||||
((TextBody)body).setCharset(charset);
|
((TextBody)body).setCharset(charset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,29 +5,29 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
public interface Part {
|
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
|
* 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
|
//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.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.fsck.k9.Account;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is an abstraction to get rid of the store- and transport-specific URIs.
|
* 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.
|
* store/transport URIs altogether.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @see Account#getStoreUri()
|
* @see com.fsck.k9.mail.store.StoreConfig#getStoreUri()
|
||||||
* @see Account#getTransportUri()
|
* @see com.fsck.k9.mail.store.StoreConfig#getTransportUri()
|
||||||
*/
|
*/
|
||||||
public class ServerSettings {
|
public class ServerSettings {
|
||||||
/**
|
/**
|
||||||
|
@ -1,24 +1,10 @@
|
|||||||
|
|
||||||
package com.fsck.k9.mail;
|
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.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
|
* 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.
|
* making as few network connections as possible.
|
||||||
*/
|
*/
|
||||||
public abstract class Store {
|
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 Folder getFolder(String name);
|
||||||
|
|
||||||
public abstract List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException;
|
public abstract List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException;
|
||||||
@ -244,14 +45,25 @@ public abstract class Store {
|
|||||||
return true;
|
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) {
|
public Pusher getPusher(PushReceiver receiver) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getAccount() {
|
protected static String decodeUtf8(String s) {
|
||||||
return mAccount;
|
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;
|
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.SmtpTransport;
|
||||||
import com.fsck.k9.mail.transport.WebDavTransport;
|
import com.fsck.k9.mail.transport.WebDavTransport;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
public abstract class Transport {
|
public abstract class Transport {
|
||||||
protected static final int SOCKET_CONNECT_TIMEOUT = 10000;
|
protected static final int SOCKET_CONNECT_TIMEOUT = 10000;
|
||||||
|
|
||||||
// RFC 1047
|
// RFC 1047
|
||||||
protected static final int SOCKET_READ_TIMEOUT = 300000;
|
protected static final int SOCKET_READ_TIMEOUT = 300000;
|
||||||
|
|
||||||
public synchronized static Transport getInstance(Account account) throws MessagingException {
|
public synchronized static Transport getInstance(StoreConfig storeConfig) throws MessagingException {
|
||||||
String uri = account.getTransportUri();
|
String uri = storeConfig.getTransportUri();
|
||||||
if (uri.startsWith("smtp")) {
|
if (uri.startsWith("smtp")) {
|
||||||
return new SmtpTransport(account);
|
return new SmtpTransport(storeConfig);
|
||||||
} else if (uri.startsWith("webdav")) {
|
} else if (uri.startsWith("webdav")) {
|
||||||
return new WebDavTransport(account);
|
return new WebDavTransport(storeConfig);
|
||||||
} else {
|
} else {
|
||||||
throw new MessagingException("Unable to locate an applicable Transport for " + uri);
|
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 sendMessage(Message message) throws MessagingException;
|
||||||
|
|
||||||
public abstract void close();
|
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$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class Base64 {
|
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.
|
* 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;
|
package com.fsck.k9.mail.internet;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.fsck.k9.K9;
|
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import java.io.ByteArrayInputStream;
|
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.codec.QuotedPrintableInputStream;
|
||||||
import org.apache.james.mime4j.util.CharsetUtil;
|
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.
|
* Static methods for decoding strings, byte arrays and encoded words.
|
||||||
@ -35,7 +36,7 @@ class DecoderUtil {
|
|||||||
|
|
||||||
Base64InputStream is = new Base64InputStream(new ByteArrayInputStream(bytes));
|
Base64InputStream is = new Base64InputStream(new ByteArrayInputStream(bytes));
|
||||||
try {
|
try {
|
||||||
return MimeUtility.readToString(is, charset);
|
return CharsetSupport.readToString(is, charset);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -68,7 +69,7 @@ class DecoderUtil {
|
|||||||
|
|
||||||
QuotedPrintableInputStream is = new QuotedPrintableInputStream(new ByteArrayInputStream(bytes));
|
QuotedPrintableInputStream is = new QuotedPrintableInputStream(new ByteArrayInputStream(bytes));
|
||||||
try {
|
try {
|
||||||
return MimeUtility.readToString(is, charset);
|
return CharsetSupport.readToString(is, charset);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -162,13 +163,13 @@ class DecoderUtil {
|
|||||||
|
|
||||||
String charset;
|
String charset;
|
||||||
try {
|
try {
|
||||||
charset = MimeUtility.fixupCharset(mimeCharset, message);
|
charset = CharsetSupport.fixupCharset(mimeCharset, message);
|
||||||
} catch (MessagingException e) {
|
} catch (MessagingException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encodedText.isEmpty()) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +178,7 @@ class DecoderUtil {
|
|||||||
} else if (encoding.equalsIgnoreCase("B")) {
|
} else if (encoding.equalsIgnoreCase("B")) {
|
||||||
return DecoderUtil.decodeB(encodedText, charset);
|
return DecoderUtil.decodeB(encodedText, charset);
|
||||||
} else {
|
} 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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ class EncoderUtil {
|
|||||||
if (charset == null)
|
if (charset == null)
|
||||||
charset = determineCharset(text);
|
charset = determineCharset(text);
|
||||||
|
|
||||||
String mimeCharset = MimeUtility.getExternalCharset(charset.name());
|
String mimeCharset = CharsetSupport.getExternalCharset(charset.name());
|
||||||
|
|
||||||
byte[] bytes = encode(text, charset);
|
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;
|
package com.fsck.k9.mail.internet;
|
||||||
|
|
||||||
import com.fsck.k9.helper.Utility;
|
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
@ -97,7 +95,7 @@ public class MimeHeader {
|
|||||||
public void writeTo(OutputStream out) throws IOException {
|
public void writeTo(OutputStream out) throws IOException {
|
||||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
|
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
|
||||||
for (Field field : mFields) {
|
for (Field field : mFields) {
|
||||||
if (!Utility.arrayContains(writeOmitFields, field.name)) {
|
if (!Arrays.asList(writeOmitFields).contains(field.name)) {
|
||||||
String v = field.value;
|
String v = field.value;
|
||||||
|
|
||||||
if (hasToBeEncoded(v)) {
|
if (hasToBeEncoded(v)) {
|
||||||
|
@ -33,8 +33,6 @@ import com.fsck.k9.mail.Message;
|
|||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Multipart;
|
import com.fsck.k9.mail.Multipart;
|
||||||
import com.fsck.k9.mail.Part;
|
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
|
* 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
|
* @param sentDate
|
||||||
* @throws com.fsck.k9.mail.MessagingException
|
* @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) {
|
if (mDateFormat == null) {
|
||||||
mDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);
|
mDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (K9.hideTimeZone()) {
|
if (hideTimeZone) {
|
||||||
mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,9 +150,9 @@ public class MimeMessage extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSentDate(Date sentDate) throws MessagingException {
|
public void setSentDate(Date sentDate, boolean hideTimeZone) throws MessagingException {
|
||||||
removeHeader("Date");
|
removeHeader("Date");
|
||||||
addSentDate(sentDate);
|
addSentDate(sentDate, hideTimeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInternalSentDate(Date sentDate) {
|
public void setInternalSentDate(Date sentDate) {
|
||||||
@ -333,7 +331,7 @@ public class MimeMessage extends Message {
|
|||||||
return "<" + UUID.randomUUID().toString().toUpperCase(Locale.US) + "@" + hostname + ">";
|
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);
|
setHeader("Message-ID", messageId);
|
||||||
mMessageId = messageId;
|
mMessageId = messageId;
|
||||||
}
|
}
|
||||||
@ -419,27 +417,27 @@ public class MimeMessage extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addHeader(String name, String value) throws UnavailableStorageException {
|
public void addHeader(String name, String value) throws MessagingException {
|
||||||
mHeader.addHeader(name, value);
|
mHeader.addHeader(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setHeader(String name, String value) throws UnavailableStorageException {
|
public void setHeader(String name, String value) throws MessagingException {
|
||||||
mHeader.setHeader(name, value);
|
mHeader.setHeader(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getHeader(String name) throws UnavailableStorageException {
|
public String[] getHeader(String name) throws MessagingException {
|
||||||
return mHeader.getHeader(name);
|
return mHeader.getHeader(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeHeader(String name) throws UnavailableStorageException {
|
public void removeHeader(String name) throws MessagingException {
|
||||||
mHeader.removeHeader(name);
|
mHeader.removeHeader(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getHeaderNames() throws UnavailableStorageException {
|
public Set<String> getHeaderNames() throws MessagingException {
|
||||||
return mHeader.getHeaderNames();
|
return mHeader.getHeaderNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,7 +472,7 @@ public class MimeMessage extends Message {
|
|||||||
if (mBody instanceof Multipart) {
|
if (mBody instanceof Multipart) {
|
||||||
((Multipart)mBody).setCharset(charset);
|
((Multipart)mBody).setCharset(charset);
|
||||||
} else if (mBody instanceof TextBody) {
|
} else if (mBody instanceof TextBody) {
|
||||||
MimeUtility.setCharset(charset, this);
|
CharsetSupport.setCharset(charset, this);
|
||||||
((TextBody)mBody).setCharset(charset);
|
((TextBody)mBody).setCharset(charset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -608,28 +606,27 @@ public class MimeMessage extends Message {
|
|||||||
/**
|
/**
|
||||||
* Copy the contents of this object into another {@code MimeMessage} object.
|
* Copy the contents of this object into another {@code MimeMessage} object.
|
||||||
*
|
*
|
||||||
* @param message
|
* @param destination The {@code MimeMessage} object to receive the contents of this instance.
|
||||||
* The {@code MimeMessage} object to receive the contents of this instance.
|
|
||||||
*/
|
*/
|
||||||
protected void copy(MimeMessage message) {
|
protected void copy(MimeMessage destination) {
|
||||||
super.copy(message);
|
super.copy(destination);
|
||||||
|
|
||||||
message.mHeader = mHeader.clone();
|
destination.mHeader = mHeader.clone();
|
||||||
|
|
||||||
message.mBody = mBody;
|
destination.mBody = mBody;
|
||||||
message.mMessageId = mMessageId;
|
destination.mMessageId = mMessageId;
|
||||||
message.mSentDate = mSentDate;
|
destination.mSentDate = mSentDate;
|
||||||
message.mDateFormat = mDateFormat;
|
destination.mDateFormat = mDateFormat;
|
||||||
message.mSize = mSize;
|
destination.mSize = mSize;
|
||||||
|
|
||||||
// These arrays are not supposed to be modified, so it's okay to reuse the references
|
// These arrays are not supposed to be modified, so it's okay to reuse the references
|
||||||
message.mFrom = mFrom;
|
destination.mFrom = mFrom;
|
||||||
message.mTo = mTo;
|
destination.mTo = mTo;
|
||||||
message.mCc = mCc;
|
destination.mCc = mCc;
|
||||||
message.mBcc = mBcc;
|
destination.mBcc = mBcc;
|
||||||
message.mReplyTo = mReplyTo;
|
destination.mReplyTo = mReplyTo;
|
||||||
message.mReferences = mReferences;
|
destination.mReferences = mReferences;
|
||||||
message.mInReplyTo = mInReplyTo;
|
destination.mInReplyTo = mInReplyTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.Body;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
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.codec.QuotedPrintableOutputStream;
|
||||||
import org.apache.james.mime4j.util.MimeUtil;
|
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.net.Socket;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
@ -20,16 +20,17 @@ import android.security.KeyChain;
|
|||||||
import android.security.KeyChainException;
|
import android.security.KeyChainException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
|
||||||
import com.fsck.k9.R;
|
import com.fsck.k9.R;
|
||||||
import com.fsck.k9.mail.CertificateValidationException;
|
import com.fsck.k9.mail.CertificateValidationException;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
|
||||||
|
import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For client certificate authentication! Provide private keys and certificates
|
* For client certificate authentication! Provide private keys and certificates
|
||||||
* during the TLS handshake using the Android 4.0 KeyChain API.
|
* during the TLS handshake using the Android 4.0 KeyChain API.
|
||||||
*/
|
*/
|
||||||
public class KeyChainKeyManager extends X509ExtendedKeyManager {
|
class KeyChainKeyManager extends X509ExtendedKeyManager {
|
||||||
|
|
||||||
private static PrivateKey sClientCertificateReferenceWorkaround;
|
private static PrivateKey sClientCertificateReferenceWorkaround;
|
||||||
|
|
||||||
@ -207,10 +208,10 @@ public class KeyChainKeyManager extends X509ExtendedKeyManager {
|
|||||||
return mAlias;
|
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;
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.fsck.k9.security;
|
package com.fsck.k9.mail.ssl;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@ -15,7 +15,7 @@ import org.apache.commons.io.IOUtils;
|
|||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
|
||||||
|
|
||||||
public class LocalKeyStore {
|
public class LocalKeyStore {
|
||||||
private static final int KEY_STORE_FILE_VERSION = 1;
|
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
|
* error, presuming setKeyStoreFile(File) is called next with a
|
||||||
* non-null File.
|
* 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;
|
mKeyStore = store;
|
||||||
mKeyStoreFile = file;
|
mKeyStoreFile = file;
|
||||||
} catch (Exception e) {
|
} 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.
|
// Use of the local key store is effectively disabled.
|
||||||
mKeyStore = null;
|
mKeyStore = null;
|
||||||
mKeyStoreFile = null;
|
mKeyStoreFile = null;
|
||||||
@ -169,7 +169,7 @@ public class LocalKeyStore {
|
|||||||
} catch (KeyStoreException e) {
|
} catch (KeyStoreException e) {
|
||||||
// Ignore: most likely there was no cert. found
|
// Ignore: most likely there was no cert. found
|
||||||
} catch (CertificateException e) {
|
} 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 android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.mail.CertificateChainException;
|
import com.fsck.k9.mail.CertificateChainException;
|
||||||
import com.fsck.k9.security.LocalKeyStore;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.TrustManager;
|
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;
|
import android.util.Log;
|
||||||
|
|
||||||
@ -19,6 +19,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter and reorder list of cipher suites and TLS versions.
|
* Filter and reorder list of cipher suites and TLS versions.
|
||||||
@ -90,7 +92,7 @@ public class TrustedSocketFactory {
|
|||||||
*/
|
*/
|
||||||
supportedProtocols = sock.getSupportedProtocols();
|
supportedProtocols = sock.getSupportedProtocols();
|
||||||
} catch (Exception e) {
|
} 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);
|
"protocols", e);
|
||||||
}
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
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 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 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);
|
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 (Exception e) {
|
||||||
// Catch everything else and save it for later.
|
// Catch everything else and save it for later.
|
||||||
mException = e;
|
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
|
// 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.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.fsck.k9.mail.store.imap;
|
package com.fsck.k9.mail.store;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods for use with IMAP.
|
* Utility methods for use with IMAP.
|
||||||
*/
|
*/
|
||||||
public class ImapUtility {
|
class ImapUtility {
|
||||||
/**
|
/**
|
||||||
* Gets all of the values in a sequence set per RFC 3501.
|
* Gets all of the values in a sequence set per RFC 3501.
|
||||||
*
|
*
|
||||||
@ -101,12 +101,12 @@ public class ImapUtility {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.d(K9.LOG_TAG, "Invalid range: " + range);
|
Log.d(LOG_TAG, "Invalid range: " + range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
Log.d(K9.LOG_TAG, "Invalid range value: " + range, e);
|
Log.d(LOG_TAG, "Invalid range value: " + range, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
@ -122,7 +122,7 @@ public class ImapUtility {
|
|||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(K9.LOG_TAG, "Invalid UID value: " + number);
|
Log.d(LOG_TAG, "Invalid UID value: " + number);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
@ -3,17 +3,14 @@ package com.fsck.k9.mail.store;
|
|||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.Account;
|
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.R;
|
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.*;
|
||||||
import com.fsck.k9.mail.filter.Base64;
|
import com.fsck.k9.mail.filter.Base64;
|
||||||
import com.fsck.k9.mail.filter.Hex;
|
import com.fsck.k9.mail.filter.Hex;
|
||||||
import com.fsck.k9.mail.internet.MimeMessage;
|
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;
|
import javax.net.ssl.SSLException;
|
||||||
|
|
||||||
@ -24,7 +21,6 @@ import java.security.MessageDigest;
|
|||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -35,7 +31,10 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
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";
|
public static final String STORE_TYPE = "POP3";
|
||||||
|
|
||||||
private static final String STLS_COMMAND = "STLS";
|
private static final String STLS_COMMAND = "STLS";
|
||||||
@ -128,12 +127,12 @@ public class Pop3Store extends Store {
|
|||||||
passwordIndex++;
|
passwordIndex++;
|
||||||
authType = AuthType.valueOf(userInfoParts[0]);
|
authType = AuthType.valueOf(userInfoParts[0]);
|
||||||
}
|
}
|
||||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[userIndex]);
|
username = decodeUtf8(userInfoParts[userIndex]);
|
||||||
if (userInfoParts.length > passwordIndex) {
|
if (userInfoParts.length > passwordIndex) {
|
||||||
if (authType == AuthType.EXTERNAL) {
|
if (authType == AuthType.EXTERNAL) {
|
||||||
clientCertificateAlias = UrlEncodingHelper.decodeUtf8(userInfoParts[passwordIndex]);
|
clientCertificateAlias = decodeUtf8(userInfoParts[passwordIndex]);
|
||||||
} else {
|
} else {
|
||||||
password = UrlEncodingHelper.decodeUtf8(userInfoParts[passwordIndex]);
|
password = decodeUtf8(userInfoParts[passwordIndex]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,11 +153,11 @@ public class Pop3Store extends Store {
|
|||||||
* @see Pop3Store#decodeUri(String)
|
* @see Pop3Store#decodeUri(String)
|
||||||
*/
|
*/
|
||||||
public static String createUri(ServerSettings server) {
|
public static String createUri(ServerSettings server) {
|
||||||
String userEnc = UrlEncodingHelper.encodeUtf8(server.username);
|
String userEnc = encodeUtf8(server.username);
|
||||||
String passwordEnc = (server.password != null) ?
|
String passwordEnc = (server.password != null) ?
|
||||||
UrlEncodingHelper.encodeUtf8(server.password) : "";
|
encodeUtf8(server.password) : "";
|
||||||
String clientCertificateAliasEnc = (server.clientCertificateAlias != null) ?
|
String clientCertificateAliasEnc = (server.clientCertificateAlias != null) ?
|
||||||
UrlEncodingHelper.encodeUtf8(server.clientCertificateAlias) : "";
|
encodeUtf8(server.clientCertificateAlias) : "";
|
||||||
|
|
||||||
String scheme;
|
String scheme;
|
||||||
switch (server.connectionSecurity) {
|
switch (server.connectionSecurity) {
|
||||||
@ -209,12 +208,12 @@ public class Pop3Store extends Store {
|
|||||||
private boolean mTopNotSupported;
|
private boolean mTopNotSupported;
|
||||||
|
|
||||||
|
|
||||||
public Pop3Store(Account account) throws MessagingException {
|
public Pop3Store(StoreConfig storeConfig) throws MessagingException {
|
||||||
super(account);
|
super(storeConfig);
|
||||||
|
|
||||||
ServerSettings settings;
|
ServerSettings settings;
|
||||||
try {
|
try {
|
||||||
settings = decodeUri(mAccount.getStoreUri());
|
settings = decodeUri(storeConfig.getStoreUri());
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new MessagingException("Error while decoding store URI", e);
|
throw new MessagingException("Error while decoding store URI", e);
|
||||||
}
|
}
|
||||||
@ -243,13 +242,13 @@ public class Pop3Store extends Store {
|
|||||||
@Override
|
@Override
|
||||||
public List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException {
|
public List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException {
|
||||||
List<Folder> folders = new LinkedList<Folder>();
|
List<Folder> folders = new LinkedList<Folder>();
|
||||||
folders.add(getFolder(mAccount.getInboxFolderName()));
|
folders.add(getFolder(mStoreConfig.getInboxFolderName()));
|
||||||
return folders;
|
return folders;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void checkSettings() throws MessagingException {
|
public void checkSettings() throws MessagingException {
|
||||||
Pop3Folder folder = new Pop3Folder(mAccount.getInboxFolderName());
|
Pop3Folder folder = new Pop3Folder(mStoreConfig.getInboxFolderName());
|
||||||
folder.open(Folder.OPEN_MODE_RW);
|
folder.open(Folder.OPEN_MODE_RW);
|
||||||
if (!mCapabilities.uidl) {
|
if (!mCapabilities.uidl) {
|
||||||
/*
|
/*
|
||||||
@ -272,7 +271,7 @@ public class Pop3Store extends Store {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Pop3Folder extends Folder {
|
class Pop3Folder extends Folder<Pop3Message> {
|
||||||
private Socket mSocket;
|
private Socket mSocket;
|
||||||
private InputStream mIn;
|
private InputStream mIn;
|
||||||
private OutputStream mOut;
|
private OutputStream mOut;
|
||||||
@ -283,11 +282,11 @@ public class Pop3Store extends Store {
|
|||||||
private int mMessageCount;
|
private int mMessageCount;
|
||||||
|
|
||||||
public Pop3Folder(String name) {
|
public Pop3Folder(String name) {
|
||||||
super(Pop3Store.this.mAccount);
|
super();
|
||||||
this.mName = name;
|
this.mName = name;
|
||||||
|
|
||||||
if (mName.equalsIgnoreCase(mAccount.getInboxFolderName())) {
|
if (mName.equalsIgnoreCase(mStoreConfig.getInboxFolderName())) {
|
||||||
mName = mAccount.getInboxFolderName();
|
mName = mStoreConfig.getInboxFolderName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,7 +296,7 @@ public class Pop3Store extends Store {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mName.equalsIgnoreCase(mAccount.getInboxFolderName())) {
|
if (!mName.equalsIgnoreCase(mStoreConfig.getInboxFolderName())) {
|
||||||
throw new MessagingException("Folder does not exist");
|
throw new MessagingException("Folder does not exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +312,7 @@ public class Pop3Store extends Store {
|
|||||||
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
|
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
|
||||||
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512);
|
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512);
|
||||||
|
|
||||||
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT);
|
mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
|
||||||
if (!isOpen()) {
|
if (!isOpen()) {
|
||||||
throw new MessagingException("Unable to connect socket");
|
throw new MessagingException("Unable to connect socket");
|
||||||
}
|
}
|
||||||
@ -328,7 +327,7 @@ public class Pop3Store extends Store {
|
|||||||
|
|
||||||
mSocket = TrustedSocketFactory.createSocket(mSocket, mHost, mPort,
|
mSocket = TrustedSocketFactory.createSocket(mSocket, mHost, mPort,
|
||||||
mClientCertificateAlias);
|
mClientCertificateAlias);
|
||||||
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT);
|
mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
|
||||||
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
|
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
|
||||||
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512);
|
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512);
|
||||||
if (!isOpen()) {
|
if (!isOpen()) {
|
||||||
@ -461,7 +460,7 @@ public class Pop3Store extends Store {
|
|||||||
try {
|
try {
|
||||||
executeSimpleCommand(
|
executeSimpleCommand(
|
||||||
String.format("AUTH EXTERNAL %s",
|
String.format("AUTH EXTERNAL %s",
|
||||||
Utility.base64Encode(mUsername)), false);
|
Base64.encode(mUsername)), false);
|
||||||
} catch (Pop3ErrorResponse e) {
|
} catch (Pop3ErrorResponse e) {
|
||||||
/*
|
/*
|
||||||
* Provide notification to the user of a problem authenticating
|
* Provide notification to the user of a problem authenticating
|
||||||
@ -541,7 +540,7 @@ public class Pop3Store extends Store {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists() throws MessagingException {
|
public boolean exists() throws MessagingException {
|
||||||
return mName.equalsIgnoreCase(mAccount.getInboxFolderName());
|
return mName.equalsIgnoreCase(mStoreConfig.getInboxFolderName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -559,7 +558,7 @@ public class Pop3Store extends Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Message getMessage(String uid) throws MessagingException {
|
public Pop3Message getMessage(String uid) throws MessagingException {
|
||||||
Pop3Message message = mUidToMsgMap.get(uid);
|
Pop3Message message = mUidToMsgMap.get(uid);
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
message = new Pop3Message(uid, this);
|
message = new Pop3Message(uid, this);
|
||||||
@ -635,7 +634,7 @@ public class Pop3Store extends Store {
|
|||||||
// response = "+OK msgNum msgUid"
|
// response = "+OK msgNum msgUid"
|
||||||
String[] uidParts = response.split(" +");
|
String[] uidParts = response.split(" +");
|
||||||
if (uidParts.length < 3 || !"+OK".equals(uidParts[0])) {
|
if (uidParts.length < 3 || !"+OK".equals(uidParts[0])) {
|
||||||
Log.e(K9.LOG_TAG, "ERR response: " + response);
|
Log.e(LOG_TAG, "ERR response: " + response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String msgUid = uidParts[2];
|
String msgUid = uidParts[2];
|
||||||
@ -693,8 +692,8 @@ public class Pop3Store extends Store {
|
|||||||
Set<String> unindexedUids = new HashSet<String>();
|
Set<String> unindexedUids = new HashSet<String>();
|
||||||
for (String uid : uids) {
|
for (String uid : uids) {
|
||||||
if (mUidToMsgMap.get(uid) == null) {
|
if (mUidToMsgMap.get(uid) == null) {
|
||||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3) {
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) {
|
||||||
Log.d(K9.LOG_TAG, "Need to index UID " + uid);
|
Log.d(LOG_TAG, "Need to index UID " + uid);
|
||||||
}
|
}
|
||||||
unindexedUids.add(uid);
|
unindexedUids.add(uid);
|
||||||
}
|
}
|
||||||
@ -719,8 +718,8 @@ public class Pop3Store extends Store {
|
|||||||
Integer msgNum = Integer.valueOf(uidParts[0]);
|
Integer msgNum = Integer.valueOf(uidParts[0]);
|
||||||
String msgUid = uidParts[1];
|
String msgUid = uidParts[1];
|
||||||
if (unindexedUids.contains(msgUid)) {
|
if (unindexedUids.contains(msgUid)) {
|
||||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3) {
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) {
|
||||||
Log.d(K9.LOG_TAG, "Got msgNum " + msgNum + " for UID " + msgUid);
|
Log.d(LOG_TAG, "Got msgNum " + msgNum + " for UID " + msgUid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pop3Message message = mUidToMsgMap.get(msgUid);
|
Pop3Message message = mUidToMsgMap.get(msgUid);
|
||||||
@ -734,8 +733,8 @@ public class Pop3Store extends Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void indexMessage(int msgNum, Pop3Message message) {
|
private void indexMessage(int msgNum, Pop3Message message) {
|
||||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3) {
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) {
|
||||||
Log.d(K9.LOG_TAG, "Adding index for UID " + message.getUid() + " to msgNum " + msgNum);
|
Log.d(LOG_TAG, "Adding index for UID " + message.getUid() + " to msgNum " + msgNum);
|
||||||
}
|
}
|
||||||
mMsgNumToMsgMap.put(msgNum, message);
|
mMsgNumToMsgMap.put(msgNum, message);
|
||||||
mUidToMsgMap.put(message.getUid(), message);
|
mUidToMsgMap.put(message.getUid(), message);
|
||||||
@ -761,7 +760,7 @@ public class Pop3Store extends Store {
|
|||||||
* @throws MessagingException
|
* @throws MessagingException
|
||||||
*/
|
*/
|
||||||
@Override
|
@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 {
|
throws MessagingException {
|
||||||
if (messages == null || messages.isEmpty()) {
|
if (messages == null || messages.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@ -805,9 +804,9 @@ public class Pop3Store extends Store {
|
|||||||
* To convert the suggested download size we take the size
|
* To convert the suggested download size we take the size
|
||||||
* divided by the maximum line size (76).
|
* divided by the maximum line size (76).
|
||||||
*/
|
*/
|
||||||
if (mAccount.getMaximumAutoDownloadMessageSize() > 0) {
|
if (mStoreConfig.getMaximumAutoDownloadMessageSize() > 0) {
|
||||||
fetchBody(pop3Message,
|
fetchBody(pop3Message,
|
||||||
(mAccount.getMaximumAutoDownloadMessageSize() / 76));
|
(mStoreConfig.getMaximumAutoDownloadMessageSize() / 76));
|
||||||
} else {
|
} else {
|
||||||
fetchBody(pop3Message, -1);
|
fetchBody(pop3Message, -1);
|
||||||
}
|
}
|
||||||
@ -819,7 +818,7 @@ public class Pop3Store extends Store {
|
|||||||
pop3Message.setBody(null);
|
pop3Message.setBody(null);
|
||||||
}
|
}
|
||||||
if (listener != null && !(fp.contains(FetchProfile.Item.ENVELOPE) && fp.size() == 1)) {
|
if (listener != null && !(fp.contains(FetchProfile.Item.ENVELOPE) && fp.size() == 1)) {
|
||||||
listener.messageFinished(message, i, count);
|
listener.messageFinished(pop3Message, i, count);
|
||||||
}
|
}
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
throw new MessagingException("Unable to fetch message", 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.
|
// Try hard to use the TOP command if we're not asked to download the whole message.
|
||||||
if (lines != -1 && (!mTopNotSupported || mCapabilities.top)) {
|
if (lines != -1 && (!mTopNotSupported || mCapabilities.top)) {
|
||||||
try {
|
try {
|
||||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3 && !mCapabilities.top) {
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3 && !mCapabilities.top) {
|
||||||
Log.d(K9.LOG_TAG, "This server doesn't support the CAPA command. " +
|
Log.d(LOG_TAG, "This server doesn't support the CAPA command. " +
|
||||||
"Checking to see if the TOP command is supported nevertheless.");
|
"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.
|
// The TOP command should be supported but something went wrong.
|
||||||
throw e;
|
throw e;
|
||||||
} else {
|
} else {
|
||||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3) {
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) {
|
||||||
Log.d(K9.LOG_TAG, "The server really doesn't support the TOP " +
|
Log.d(LOG_TAG, "The server really doesn't support the TOP " +
|
||||||
"command. Using RETR instead.");
|
"command. Using RETR instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1029,8 +1028,8 @@ public class Pop3Store extends Store {
|
|||||||
}
|
}
|
||||||
} while ((d = mIn.read()) != -1);
|
} while ((d = mIn.read()) != -1);
|
||||||
String ret = sb.toString();
|
String ret = sb.toString();
|
||||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3) {
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) {
|
||||||
Log.d(K9.LOG_TAG, "<<< " + ret);
|
Log.d(LOG_TAG, "<<< " + ret);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1122,12 +1121,12 @@ public class Pop3Store extends Store {
|
|||||||
open(Folder.OPEN_MODE_RW);
|
open(Folder.OPEN_MODE_RW);
|
||||||
|
|
||||||
if (command != null) {
|
if (command != null) {
|
||||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_POP3) {
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) {
|
||||||
if (sensitive && !K9.DEBUG_SENSITIVE) {
|
if (sensitive && !K9MailLib.isDebugSensitive()) {
|
||||||
Log.d(K9.LOG_TAG, ">>> "
|
Log.d(LOG_TAG, ">>> "
|
||||||
+ "[Command Hidden, Enable Sensitive Debug Logging To Show]");
|
+ "[Command Hidden, Enable Sensitive Debug Logging To Show]");
|
||||||
} else {
|
} else {
|
||||||
Log.d(K9.LOG_TAG, ">>> " + command);
|
Log.d(LOG_TAG, ">>> " + command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1199,7 +1198,7 @@ public class Pop3Store extends Store {
|
|||||||
// }
|
// }
|
||||||
// catch (MessagingException me)
|
// 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.conn.scheme.LayeredSocketFactory;
|
||||||
import org.apache.http.params.HttpParams;
|
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.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
@ -2,15 +2,11 @@ package com.fsck.k9.mail.store;
|
|||||||
|
|
||||||
import android.util.Log;
|
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.*;
|
||||||
|
import com.fsck.k9.mail.filter.Base64;
|
||||||
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
||||||
import com.fsck.k9.mail.internet.MimeMessage;
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
|
import com.fsck.k9.mail.MessageRetrievalListener;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.*;
|
import org.apache.http.*;
|
||||||
@ -49,13 +45,16 @@ import java.text.SimpleDateFormat;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.zip.GZIPInputStream;
|
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>
|
* <pre>
|
||||||
* Uses WebDAV formatted HTTP calls to an MS Exchange server to fetch email
|
* Uses WebDAV formatted HTTP calls to an MS Exchange server to fetch email
|
||||||
* and email information.
|
* and email information.
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public class WebDavStore extends Store {
|
public class WebDavStore extends RemoteStore {
|
||||||
public static final String STORE_TYPE = "WebDAV";
|
public static final String STORE_TYPE = "WebDAV";
|
||||||
|
|
||||||
// Authentication types
|
// Authentication types
|
||||||
@ -138,7 +137,7 @@ public class WebDavStore extends Store {
|
|||||||
String userInfo = webDavUri.getUserInfo();
|
String userInfo = webDavUri.getUserInfo();
|
||||||
if (userInfo != null) {
|
if (userInfo != null) {
|
||||||
String[] userInfoParts = userInfo.split(":");
|
String[] userInfoParts = userInfo.split(":");
|
||||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]);
|
username = decodeUtf8(userInfoParts[0]);
|
||||||
String userParts[] = username.split("\\\\", 2);
|
String userParts[] = username.split("\\\\", 2);
|
||||||
|
|
||||||
if (userParts.length > 1) {
|
if (userParts.length > 1) {
|
||||||
@ -147,7 +146,7 @@ public class WebDavStore extends Store {
|
|||||||
alias = username;
|
alias = username;
|
||||||
}
|
}
|
||||||
if (userInfoParts.length > 1) {
|
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)
|
* @see WebDavStore#decodeUri(String)
|
||||||
*/
|
*/
|
||||||
public static String createUri(ServerSettings server) {
|
public static String createUri(ServerSettings server) {
|
||||||
String userEnc = UrlEncodingHelper.encodeUtf8(server.username);
|
String userEnc = encodeUtf8(server.username);
|
||||||
String passwordEnc = (server.password != null) ?
|
String passwordEnc = (server.password != null) ?
|
||||||
UrlEncodingHelper.encodeUtf8(server.password) : "";
|
encodeUtf8(server.password) : "";
|
||||||
|
|
||||||
String scheme;
|
String scheme;
|
||||||
switch (server.connectionSecurity) {
|
switch (server.connectionSecurity) {
|
||||||
@ -294,12 +293,12 @@ public class WebDavStore extends Store {
|
|||||||
private Map<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
|
private Map<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
|
||||||
|
|
||||||
|
|
||||||
public WebDavStore(Account account) throws MessagingException {
|
public WebDavStore(StoreConfig storeConfig) throws MessagingException {
|
||||||
super(account);
|
super(storeConfig);
|
||||||
|
|
||||||
WebDavStoreSettings settings;
|
WebDavStoreSettings settings;
|
||||||
try {
|
try {
|
||||||
settings = decodeUri(mAccount.getStoreUri());
|
settings = decodeUri(storeConfig.getStoreUri());
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new MessagingException("Error while decoding store URI", 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".
|
// The inbox path would look like: "https://mail.domain.com/Exchange/alias/Inbox".
|
||||||
mUrl = getRoot() + mPath + mMailboxPath;
|
mUrl = getRoot() + mPath + mMailboxPath;
|
||||||
|
|
||||||
mAuthString = "Basic " + Utility.base64Encode(mUsername + ":" + mPassword);
|
mAuthString = "Basic " + Base64.encode(mUsername + ":" + mPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getRoot() {
|
private String getRoot() {
|
||||||
@ -380,21 +379,21 @@ public class WebDavStore extends Store {
|
|||||||
Map<String, String> specialFoldersMap = dataset.getSpecialFolderToUrl();
|
Map<String, String> specialFoldersMap = dataset.getSpecialFolderToUrl();
|
||||||
String folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_INBOX_FOLDER));
|
String folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_INBOX_FOLDER));
|
||||||
if (folderName != null) {
|
if (folderName != null) {
|
||||||
mAccount.setAutoExpandFolderName(folderName);
|
mStoreConfig.setAutoExpandFolderName(folderName);
|
||||||
mAccount.setInboxFolderName(folderName);
|
mStoreConfig.setInboxFolderName(folderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_DRAFTS_FOLDER));
|
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_DRAFTS_FOLDER));
|
||||||
if (folderName != null)
|
if (folderName != null)
|
||||||
mAccount.setDraftsFolderName(folderName);
|
mStoreConfig.setDraftsFolderName(folderName);
|
||||||
|
|
||||||
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_TRASH_FOLDER));
|
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_TRASH_FOLDER));
|
||||||
if (folderName != null)
|
if (folderName != null)
|
||||||
mAccount.setTrashFolderName(folderName);
|
mStoreConfig.setTrashFolderName(folderName);
|
||||||
|
|
||||||
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SPAM_FOLDER));
|
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SPAM_FOLDER));
|
||||||
if (folderName != null)
|
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.
|
// 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));
|
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SENT_FOLDER));
|
||||||
if (folderName != null)
|
if (folderName != null)
|
||||||
mAccount.setSentFolderName(folderName);
|
mStoreConfig.setSentFolderName(folderName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Next we get all the folders (including "special" ones)
|
* 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"
|
// Decodes the url-encoded folder name (i.e. "My%20folder" => "My Folder"
|
||||||
|
|
||||||
return UrlEncodingHelper.decodeUtf8(fullPathName);
|
return decodeUtf8(fullPathName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -721,7 +720,7 @@ public class WebDavStore extends Store {
|
|||||||
doFBA(null);
|
doFBA(null);
|
||||||
}
|
}
|
||||||
} catch (IOException ioe) {
|
} 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);
|
throw new MessagingException("Error during authentication", ioe);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -791,7 +790,7 @@ public class WebDavStore extends Store {
|
|||||||
} catch (SSLException e) {
|
} catch (SSLException e) {
|
||||||
throw new CertificateValidationException(e.getMessage(), e);
|
throw new CertificateValidationException(e.getMessage(), e);
|
||||||
} catch (IOException ioe) {
|
} 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);
|
throw new MessagingException("IOException", ioe);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -893,7 +892,7 @@ public class WebDavStore extends Store {
|
|||||||
response = httpClient.executeOverride(request, mContext);
|
response = httpClient.executeOverride(request, mContext);
|
||||||
authenticated = testAuthenticationResponse(response);
|
authenticated = testAuthenticationResponse(response);
|
||||||
} catch (URISyntaxException e) {
|
} 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);
|
throw new MessagingException("URISyntaxException caught", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -987,7 +986,7 @@ public class WebDavStore extends Store {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (URISyntaxException e) {
|
} 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);
|
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);
|
Scheme s = new Scheme("https", new WebDavSocketFactory(mHost, 443), 443);
|
||||||
reg.register(s);
|
reg.register(s);
|
||||||
} catch (NoSuchAlgorithmException nsa) {
|
} 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);
|
throw new MessagingException("NoSuchAlgorithmException in getHttpClient: " + nsa);
|
||||||
} catch (KeyManagementException kme) {
|
} 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);
|
throw new MessagingException("KeyManagementException in getHttpClient: " + kme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1094,10 +1093,10 @@ public class WebDavStore extends Store {
|
|||||||
istream = WebDavHttpClient.getUngzippedContent(entity);
|
istream = WebDavHttpClient.getUngzippedContent(entity);
|
||||||
}
|
}
|
||||||
} catch (UnsupportedEncodingException uee) {
|
} 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);
|
throw new MessagingException("UnsupportedEncodingException", uee);
|
||||||
} catch (IOException ioe) {
|
} 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);
|
throw new MessagingException("IOException", ioe);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1122,8 +1121,8 @@ public class WebDavStore extends Store {
|
|||||||
boolean needsParsing)
|
boolean needsParsing)
|
||||||
throws MessagingException {
|
throws MessagingException {
|
||||||
DataSet dataset = new DataSet();
|
DataSet dataset = new DataSet();
|
||||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_WEBDAV) {
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_WEBDAV) {
|
||||||
Log.v(K9.LOG_TAG, "processRequest url = '" + url + "', method = '" + method + "', messageBody = '"
|
Log.v(LOG_TAG, "processRequest url = '" + url + "', method = '" + method + "', messageBody = '"
|
||||||
+ messageBody + "'");
|
+ messageBody + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1155,10 +1154,10 @@ public class WebDavStore extends Store {
|
|||||||
|
|
||||||
dataset = myHandler.getDataSet();
|
dataset = myHandler.getDataSet();
|
||||||
} catch (SAXException se) {
|
} 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);
|
throw new MessagingException("SAXException in processRequest() ", se);
|
||||||
} catch (ParserConfigurationException pce) {
|
} catch (ParserConfigurationException pce) {
|
||||||
Log.e(K9.LOG_TAG, "ParserConfigurationException in processRequest() " + pce + "\nTrace: "
|
Log.e(LOG_TAG, "ParserConfigurationException in processRequest() " + pce + "\nTrace: "
|
||||||
+ processException(pce));
|
+ processException(pce));
|
||||||
throw new MessagingException("ParserConfigurationException in processRequest() ", pce);
|
throw new MessagingException("ParserConfigurationException in processRequest() ", pce);
|
||||||
}
|
}
|
||||||
@ -1166,10 +1165,10 @@ public class WebDavStore extends Store {
|
|||||||
istream.close();
|
istream.close();
|
||||||
}
|
}
|
||||||
} catch (UnsupportedEncodingException uee) {
|
} 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);
|
throw new MessagingException("UnsupportedEncodingException in processRequest() ", uee);
|
||||||
} catch (IOException ioe) {
|
} 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);
|
throw new MessagingException("IOException in processRequest() ", ioe);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1196,7 +1195,7 @@ public class WebDavStore extends Store {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMessages(List<? extends Message> messages) throws MessagingException {
|
public void sendMessages(List<? extends Message> messages) throws MessagingException {
|
||||||
WebDavFolder tmpFolder = (WebDavStore.WebDavFolder) getFolder(mAccount.getDraftsFolderName());
|
WebDavFolder tmpFolder = (WebDavStore.WebDavFolder) getFolder(mStoreConfig.getDraftsFolderName());
|
||||||
try {
|
try {
|
||||||
tmpFolder.open(Folder.OPEN_MODE_RW);
|
tmpFolder.open(Folder.OPEN_MODE_RW);
|
||||||
List<? extends Message> retMessages = tmpFolder.appendWebDavMessages(messages);
|
List<? extends Message> retMessages = tmpFolder.appendWebDavMessages(messages);
|
||||||
@ -1216,7 +1215,7 @@ public class WebDavStore extends Store {
|
|||||||
/**
|
/**
|
||||||
* A WebDav Folder
|
* A WebDav Folder
|
||||||
*/
|
*/
|
||||||
class WebDavFolder extends Folder {
|
class WebDavFolder extends Folder<WebDavMessage> {
|
||||||
private String mName;
|
private String mName;
|
||||||
private String mFolderUrl;
|
private String mFolderUrl;
|
||||||
private boolean mIsOpen = false;
|
private boolean mIsOpen = false;
|
||||||
@ -1229,7 +1228,7 @@ public class WebDavStore extends Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public WebDavFolder(WebDavStore nStore, String name) {
|
public WebDavFolder(WebDavStore nStore, String name) {
|
||||||
super(nStore.getAccount());
|
super();
|
||||||
store = nStore;
|
store = nStore;
|
||||||
this.mName = name;
|
this.mName = name;
|
||||||
|
|
||||||
@ -1238,9 +1237,9 @@ public class WebDavStore extends Store {
|
|||||||
String url = "";
|
String url = "";
|
||||||
for (int i = 0, count = urlParts.length; i < count; i++) {
|
for (int i = 0, count = urlParts.length; i < count; i++) {
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
url = url + "/" + UrlEncodingHelper.encodeUtf8(urlParts[i]);
|
url = url + "/" + encodeUtf8(urlParts[i]);
|
||||||
} else {
|
} else {
|
||||||
url = UrlEncodingHelper.encodeUtf8(urlParts[i]);
|
url = encodeUtf8(urlParts[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
encodedName = url;
|
encodedName = url;
|
||||||
@ -1310,7 +1309,7 @@ public class WebDavStore extends Store {
|
|||||||
headers.put("Brief", "t");
|
headers.put("Brief", "t");
|
||||||
headers.put("If-Match", "*");
|
headers.put("If-Match", "*");
|
||||||
String action = (isMove ? "BMOVE" : "BCOPY");
|
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);
|
processRequest(mFolderUrl, action, messageBody, headers, false);
|
||||||
}
|
}
|
||||||
@ -1333,8 +1332,8 @@ public class WebDavStore extends Store {
|
|||||||
if (dataset != null) {
|
if (dataset != null) {
|
||||||
messageCount = dataset.getMessageCount();
|
messageCount = dataset.getMessageCount();
|
||||||
}
|
}
|
||||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_WEBDAV) {
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_WEBDAV) {
|
||||||
Log.v(K9.LOG_TAG, "Counted messages and webdav returned: "+messageCount);
|
Log.v(LOG_TAG, "Counted messages and webdav returned: "+messageCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
return messageCount;
|
return messageCount;
|
||||||
@ -1397,7 +1396,7 @@ public class WebDavStore extends Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Message getMessage(String uid) throws MessagingException {
|
public WebDavMessage getMessage(String uid) throws MessagingException {
|
||||||
return new WebDavMessage(uid, this);
|
return new WebDavMessage(uid, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1498,7 +1497,7 @@ public class WebDavStore extends Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 {
|
throws MessagingException {
|
||||||
if (messages == null ||
|
if (messages == null ||
|
||||||
messages.isEmpty()) {
|
messages.isEmpty()) {
|
||||||
@ -1519,8 +1518,8 @@ public class WebDavStore extends Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fp.contains(FetchProfile.Item.BODY_SANE)) {
|
if (fp.contains(FetchProfile.Item.BODY_SANE)) {
|
||||||
if (mAccount.getMaximumAutoDownloadMessageSize() > 0) {
|
if (mStoreConfig.getMaximumAutoDownloadMessageSize() > 0) {
|
||||||
fetchMessages(messages, listener, (mAccount.getMaximumAutoDownloadMessageSize() / 76));
|
fetchMessages(messages, listener, (mStoreConfig.getMaximumAutoDownloadMessageSize() / 76));
|
||||||
} else {
|
} else {
|
||||||
fetchMessages(messages, listener, -1);
|
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.
|
* 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 {
|
throws MessagingException {
|
||||||
WebDavHttpClient httpclient;
|
WebDavHttpClient httpclient;
|
||||||
httpclient = getHttpClient();
|
httpclient = getHttpClient();
|
||||||
@ -1561,7 +1560,7 @@ public class WebDavStore extends Store {
|
|||||||
*/
|
*/
|
||||||
if (wdMessage.getUrl().equals("")) {
|
if (wdMessage.getUrl().equals("")) {
|
||||||
wdMessage.setUrl(getMessageUrls(new String[] { wdMessage.getUid() }).get(wdMessage.getUid()));
|
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() + "'");
|
+ wdMessage.getUrl() + "'");
|
||||||
if (wdMessage.getUrl().equals("")) {
|
if (wdMessage.getUrl().equals("")) {
|
||||||
throw new MessagingException("Unable to get URL for message");
|
throw new MessagingException("Unable to get URL for message");
|
||||||
@ -1569,7 +1568,7 @@ public class WebDavStore extends Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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() + "'");
|
+ wdMessage.getUrl() + "'");
|
||||||
HttpGet httpget = new HttpGet(new URI(wdMessage.getUrl()));
|
HttpGet httpget = new HttpGet(new URI(wdMessage.getUrl()));
|
||||||
HttpResponse response;
|
HttpResponse response;
|
||||||
@ -1594,8 +1593,8 @@ public class WebDavStore extends Store {
|
|||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
InputStream istream = null;
|
InputStream istream = null;
|
||||||
StringBuilder buffer = new StringBuilder();
|
StringBuilder buffer = new StringBuilder();
|
||||||
String tempText = "";
|
String tempText;
|
||||||
String resultText = "";
|
String resultText;
|
||||||
BufferedReader reader = null;
|
BufferedReader reader = null;
|
||||||
int currentLines = 0;
|
int currentLines = 0;
|
||||||
|
|
||||||
@ -1625,13 +1624,13 @@ public class WebDavStore extends Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (IllegalArgumentException iae) {
|
} 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);
|
throw new MessagingException("IllegalArgumentException caught", iae);
|
||||||
} catch (URISyntaxException use) {
|
} 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);
|
throw new MessagingException("URISyntaxException caught", use);
|
||||||
} catch (IOException ioe) {
|
} 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: "
|
+ "\nURL: " + wdMessage.getUrl() + "\nError: " + ioe.getMessage() + "\nTrace: "
|
||||||
+ processException(ioe));
|
+ processException(ioe));
|
||||||
throw new MessagingException("Failure code " + statusCode, ioe);
|
throw new MessagingException("Failure code " + statusCode, ioe);
|
||||||
@ -1702,7 +1701,7 @@ public class WebDavStore extends Store {
|
|||||||
try {
|
try {
|
||||||
wdMessage.setFlagInternal(Flag.SEEN, uidToReadStatus.get(wdMessage.getUid()));
|
wdMessage.setFlagInternal(Flag.SEEN, uidToReadStatus.get(wdMessage.getUid()));
|
||||||
} catch (NullPointerException e) {
|
} 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) {
|
if (listener != null) {
|
||||||
@ -1771,7 +1770,7 @@ public class WebDavStore extends Store {
|
|||||||
wdMessage.setNewHeaders(envelope);
|
wdMessage.setNewHeaders(envelope);
|
||||||
wdMessage.setFlagInternal(Flag.SEEN, envelope.getReadStatus());
|
wdMessage.setFlagInternal(Flag.SEEN, envelope.getReadStatus());
|
||||||
} else {
|
} 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) {
|
if (listener != null) {
|
||||||
@ -1881,9 +1880,9 @@ public class WebDavStore extends Store {
|
|||||||
if (!messageURL.endsWith("/")) {
|
if (!messageURL.endsWith("/")) {
|
||||||
messageURL += "/";
|
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 = new HttpGeneric(messageURL);
|
||||||
httpmethod.setMethod("PUT");
|
httpmethod.setMethod("PUT");
|
||||||
@ -1922,7 +1921,7 @@ public class WebDavStore extends Store {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUidFromMessageId(Message message) throws MessagingException {
|
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 "
|
"Unimplemented method getUidFromMessageId in WebDavStore.WebDavFolder could lead to duplicate messages "
|
||||||
+ " being uploaded to the Sent folder");
|
+ " being uploaded to the Sent folder");
|
||||||
return null;
|
return null;
|
||||||
@ -1930,7 +1929,7 @@ public class WebDavStore extends Store {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setFlags(final Set<Flag> flags, boolean value) throws MessagingException {
|
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");
|
"Unimplemented method setFlags(Set<Flag>, boolean) breaks markAllMessagesAsRead and EmptyTrash");
|
||||||
// Try to make this efficient by not retrieving all of the messages
|
// 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
|
* We have to decode, then encode the URL because Exchange likes to not properly encode all characters
|
||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
end = UrlEncodingHelper.decodeUtf8(end);
|
end = decodeUtf8(end);
|
||||||
end = UrlEncodingHelper.encodeUtf8(end);
|
end = encodeUtf8(end);
|
||||||
end = end.replaceAll("\\+", "%20");
|
end = end.replaceAll("\\+", "%20");
|
||||||
} catch (IllegalArgumentException iae) {
|
} 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));
|
+ processException(iae));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2022,7 +2021,7 @@ public class WebDavStore extends Store {
|
|||||||
@Override
|
@Override
|
||||||
public void delete(String trashFolderName) throws MessagingException {
|
public void delete(String trashFolderName) throws MessagingException {
|
||||||
WebDavFolder wdFolder = (WebDavFolder) getFolder();
|
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));
|
wdFolder.moveMessages(Collections.singletonList(this), wdFolder.getStore().getFolder(trashFolderName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2320,7 +2319,7 @@ public class WebDavStore extends Store {
|
|||||||
Date parsedDate = dfInput.parse(date);
|
Date parsedDate = dfInput.parse(date);
|
||||||
tempDate = dfOutput.format(parsedDate);
|
tempDate = dfOutput.format(parsedDate);
|
||||||
} catch (java.text.ParseException pe) {
|
} 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);
|
envelope.addHeader(header, tempDate);
|
||||||
} else {
|
} else {
|
||||||
@ -2359,8 +2358,8 @@ public class WebDavStore extends Store {
|
|||||||
public HttpGeneric(final String uri) {
|
public HttpGeneric(final String uri) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
if (K9.DEBUG) {
|
if (K9MailLib.isDebug()) {
|
||||||
Log.v(K9.LOG_TAG, "Starting uri = '" + uri + "'");
|
Log.v(LOG_TAG, "Starting uri = '" + uri + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] urlParts = uri.split("/");
|
String[] urlParts = uri.split("/");
|
||||||
@ -2373,12 +2372,12 @@ public class WebDavStore extends Store {
|
|||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
if (length > 3) {
|
if (length > 3) {
|
||||||
end = UrlEncodingHelper.decodeUtf8(end);
|
end = decodeUtf8(end);
|
||||||
end = UrlEncodingHelper.encodeUtf8(end);
|
end = encodeUtf8(end);
|
||||||
end = end.replaceAll("\\+", "%20");
|
end = end.replaceAll("\\+", "%20");
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException iae) {
|
} 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));
|
+ processException(iae));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2389,13 +2388,13 @@ public class WebDavStore extends Store {
|
|||||||
url = urlParts[i];
|
url = urlParts[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_WEBDAV) {
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_WEBDAV) {
|
||||||
Log.v(K9.LOG_TAG, "url = '" + url + "' length = " + url.length()
|
Log.v(LOG_TAG, "url = '" + url + "' length = " + url.length()
|
||||||
+ ", end = '" + end + "' length = " + end.length());
|
+ ", end = '" + end + "' length = " + end.length());
|
||||||
}
|
}
|
||||||
url = url + "/" + end;
|
url = url + "/" + end;
|
||||||
|
|
||||||
Log.i(K9.LOG_TAG, "url = " + url);
|
Log.i(LOG_TAG, "url = " + url);
|
||||||
setURI(URI.create(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.
|
* the License for the specific language governing permissions and limitations under the License.
|
||||||
*/
|
*/
|
||||||
public static void modifyRequestToAcceptGzipResponse(HttpRequest request) {
|
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");
|
request.addHeader("Accept-Encoding", "gzip");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2441,7 +2440,7 @@ public class WebDavStore extends Store {
|
|||||||
if (contentEncoding == null)
|
if (contentEncoding == null)
|
||||||
return responseStream;
|
return responseStream;
|
||||||
if (contentEncoding.contains("gzip")) {
|
if (contentEncoding.contains("gzip")) {
|
||||||
Log.i(K9.LOG_TAG, "Response is gzipped");
|
Log.i(LOG_TAG, "Response is gzipped");
|
||||||
responseStream = new GZIPInputStream(responseStream);
|
responseStream = new GZIPInputStream(responseStream);
|
||||||
}
|
}
|
||||||
return responseStream;
|
return responseStream;
|
||||||
|
@ -3,20 +3,18 @@ package com.fsck.k9.mail.transport;
|
|||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.Account;
|
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.R;
|
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.*;
|
||||||
import com.fsck.k9.mail.Message.RecipientType;
|
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.EOLConvertingOutputStream;
|
||||||
import com.fsck.k9.mail.filter.LineWrapOutputStream;
|
import com.fsck.k9.mail.filter.LineWrapOutputStream;
|
||||||
import com.fsck.k9.mail.filter.PeekableInputStream;
|
import com.fsck.k9.mail.filter.PeekableInputStream;
|
||||||
import com.fsck.k9.mail.filter.SmtpDataStuffing;
|
import com.fsck.k9.mail.filter.SmtpDataStuffing;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.CharsetSupport;
|
||||||
import com.fsck.k9.mail.store.local.LocalMessage;
|
import com.fsck.k9.mail.store.StoreConfig;
|
||||||
import com.fsck.k9.net.ssl.TrustedSocketFactory;
|
import com.fsck.k9.mail.ssl.TrustedSocketFactory;
|
||||||
|
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
|
|
||||||
@ -28,6 +26,9 @@ import java.net.*;
|
|||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.*;
|
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 class SmtpTransport extends Transport {
|
||||||
public static final String TRANSPORT_TYPE = "SMTP";
|
public static final String TRANSPORT_TYPE = "SMTP";
|
||||||
|
|
||||||
@ -95,19 +96,19 @@ public class SmtpTransport extends Transport {
|
|||||||
String[] userInfoParts = smtpUri.getUserInfo().split(":");
|
String[] userInfoParts = smtpUri.getUserInfo().split(":");
|
||||||
if (userInfoParts.length == 1) {
|
if (userInfoParts.length == 1) {
|
||||||
authType = AuthType.PLAIN;
|
authType = AuthType.PLAIN;
|
||||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]);
|
username = decodeUtf8(userInfoParts[0]);
|
||||||
} else if (userInfoParts.length == 2) {
|
} else if (userInfoParts.length == 2) {
|
||||||
authType = AuthType.PLAIN;
|
authType = AuthType.PLAIN;
|
||||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]);
|
username = decodeUtf8(userInfoParts[0]);
|
||||||
password = UrlEncodingHelper.decodeUtf8(userInfoParts[1]);
|
password = decodeUtf8(userInfoParts[1]);
|
||||||
} else if (userInfoParts.length == 3) {
|
} else if (userInfoParts.length == 3) {
|
||||||
// NOTE: In SmptTransport URIs, the authType comes last!
|
// NOTE: In SmptTransport URIs, the authType comes last!
|
||||||
authType = AuthType.valueOf(userInfoParts[2]);
|
authType = AuthType.valueOf(userInfoParts[2]);
|
||||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]);
|
username = decodeUtf8(userInfoParts[0]);
|
||||||
if (authType == AuthType.EXTERNAL) {
|
if (authType == AuthType.EXTERNAL) {
|
||||||
clientCertificateAlias = UrlEncodingHelper.decodeUtf8(userInfoParts[1]);
|
clientCertificateAlias = decodeUtf8(userInfoParts[1]);
|
||||||
} else {
|
} 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.
|
* @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)
|
* @see SmtpTransport#decodeUri(String)
|
||||||
*/
|
*/
|
||||||
public static String createUri(ServerSettings server) {
|
public static String createUri(ServerSettings server) {
|
||||||
String userEnc = (server.username != null) ?
|
String userEnc = (server.username != null) ?
|
||||||
UrlEncodingHelper.encodeUtf8(server.username) : "";
|
encodeUtf8(server.username) : "";
|
||||||
String passwordEnc = (server.password != null) ?
|
String passwordEnc = (server.password != null) ?
|
||||||
UrlEncodingHelper.encodeUtf8(server.password) : "";
|
encodeUtf8(server.password) : "";
|
||||||
String clientCertificateAliasEnc = (server.clientCertificateAlias != null) ?
|
String clientCertificateAliasEnc = (server.clientCertificateAlias != null) ?
|
||||||
UrlEncodingHelper.encodeUtf8(server.clientCertificateAlias) : "";
|
encodeUtf8(server.clientCertificateAlias) : "";
|
||||||
|
|
||||||
String scheme;
|
String scheme;
|
||||||
switch (server.connectionSecurity) {
|
switch (server.connectionSecurity) {
|
||||||
@ -149,7 +150,7 @@ public class SmtpTransport extends Transport {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
String userInfo = null;
|
String userInfo;
|
||||||
AuthType authType = server.authenticationType;
|
AuthType authType = server.authenticationType;
|
||||||
// NOTE: authType is append at last item, in contrast to ImapStore and Pop3Store!
|
// NOTE: authType is append at last item, in contrast to ImapStore and Pop3Store!
|
||||||
if (authType != null) {
|
if (authType != null) {
|
||||||
@ -183,10 +184,10 @@ public class SmtpTransport extends Transport {
|
|||||||
private boolean m8bitEncodingAllowed;
|
private boolean m8bitEncodingAllowed;
|
||||||
private int mLargestAcceptableMessage;
|
private int mLargestAcceptableMessage;
|
||||||
|
|
||||||
public SmtpTransport(Account account) throws MessagingException {
|
public SmtpTransport(StoreConfig storeConfig) throws MessagingException {
|
||||||
ServerSettings settings;
|
ServerSettings settings;
|
||||||
try {
|
try {
|
||||||
settings = decodeUri(account.getTransportUri());
|
settings = decodeUri(storeConfig.getTransportUri());
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new MessagingException("Error while decoding transport URI", e);
|
throw new MessagingException("Error while decoding transport URI", e);
|
||||||
}
|
}
|
||||||
@ -305,8 +306,8 @@ public class SmtpTransport extends Transport {
|
|||||||
try {
|
try {
|
||||||
mLargestAcceptableMessage = Integer.parseInt(extensions.get("SIZE"));
|
mLargestAcceptableMessage = Integer.parseInt(extensions.get("SIZE"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_SMTP) {
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_SMTP) {
|
||||||
Log.d(K9.LOG_TAG, "Tried to parse " + extensions.get("SIZE") + " and get an int", e);
|
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]);
|
extensions.put(pair[0].toUpperCase(Locale.US), pair.length == 1 ? "" : pair[1]);
|
||||||
}
|
}
|
||||||
} catch (NegativeSmtpReplyException e) {
|
} catch (NegativeSmtpReplyException e) {
|
||||||
if (K9.DEBUG) {
|
if (K9MailLib.isDebug()) {
|
||||||
Log.v(K9.LOG_TAG, "Server doesn't support the EHLO command. Trying HELO...");
|
Log.v(LOG_TAG, "Server doesn't support the EHLO command. Trying HELO...");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
executeSimpleCommand("HELO " + host);
|
executeSimpleCommand("HELO " + host);
|
||||||
} catch (NegativeSmtpReplyException e2) {
|
} 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;
|
return extensions;
|
||||||
@ -465,7 +466,7 @@ public class SmtpTransport extends Transport {
|
|||||||
new HashMap<String, List<String>>();
|
new HashMap<String, List<String>>();
|
||||||
for (Address address : addresses) {
|
for (Address address : addresses) {
|
||||||
String addressString = address.getAddress();
|
String addressString = address.getAddress();
|
||||||
String charset = MimeUtility.getCharsetFromAddress(addressString);
|
String charset = CharsetSupport.getCharsetFromAddress(addressString);
|
||||||
List<String> addressesOfCharset = charsetAddressesMap.get(charset);
|
List<String> addressesOfCharset = charsetAddressesMap.get(charset);
|
||||||
if (addressesOfCharset == null) {
|
if (addressesOfCharset == null) {
|
||||||
addressesOfCharset = new ArrayList<String>();
|
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
|
// 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
|
// 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) {
|
if (message.calculateSize() > mLargestAcceptableMessage) {
|
||||||
MessagingException me = new MessagingException("Message too large for server");
|
MessagingException me = new MessagingException("Message too large for server");
|
||||||
//TODO this looks rather suspicious... shouldn't it be true?
|
//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
|
// "5xx text" -responses are permanent failures
|
||||||
String msg = e.getMessage();
|
String msg = e.getMessage();
|
||||||
if (msg != null && msg.startsWith("5")) {
|
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;
|
possibleSend = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,21 +583,21 @@ public class SmtpTransport extends Transport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
String ret = sb.toString();
|
String ret = sb.toString();
|
||||||
if (K9.DEBUG && K9.DEBUG_PROTOCOL_SMTP)
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_SMTP)
|
||||||
Log.d(K9.LOG_TAG, "SMTP <<< " + ret);
|
Log.d(LOG_TAG, "SMTP <<< " + ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeLine(String s, boolean sensitive) throws IOException {
|
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;
|
final String commandToLog;
|
||||||
if (sensitive && !K9.DEBUG_SENSITIVE) {
|
if (sensitive && !K9MailLib.isDebugSensitive()) {
|
||||||
commandToLog = "SMTP >>> *sensitive*";
|
commandToLog = "SMTP >>> *sensitive*";
|
||||||
} else {
|
} else {
|
||||||
commandToLog = "SMTP >>> " + s;
|
commandToLog = "SMTP >>> " + s;
|
||||||
}
|
}
|
||||||
Log.d(K9.LOG_TAG, commandToLog);
|
Log.d(LOG_TAG, commandToLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] data = s.concat("\r\n").getBytes();
|
byte[] data = s.concat("\r\n").getBytes();
|
||||||
@ -701,8 +702,8 @@ public class SmtpTransport extends Transport {
|
|||||||
AuthenticationFailedException, IOException {
|
AuthenticationFailedException, IOException {
|
||||||
try {
|
try {
|
||||||
executeSimpleCommand("AUTH LOGIN");
|
executeSimpleCommand("AUTH LOGIN");
|
||||||
executeSimpleCommand(Utility.base64Encode(username), true);
|
executeSimpleCommand(Base64.encode(username), true);
|
||||||
executeSimpleCommand(Utility.base64Encode(password), true);
|
executeSimpleCommand(Base64.encode(password), true);
|
||||||
} catch (NegativeSmtpReplyException exception) {
|
} catch (NegativeSmtpReplyException exception) {
|
||||||
if (exception.getReplyCode() == 535) {
|
if (exception.getReplyCode() == 535) {
|
||||||
// Authentication credentials invalid
|
// Authentication credentials invalid
|
||||||
@ -716,7 +717,7 @@ public class SmtpTransport extends Transport {
|
|||||||
|
|
||||||
private void saslAuthPlain(String username, String password) throws MessagingException,
|
private void saslAuthPlain(String username, String password) throws MessagingException,
|
||||||
AuthenticationFailedException, IOException {
|
AuthenticationFailedException, IOException {
|
||||||
String data = Utility.base64Encode("\000" + username + "\000" + password);
|
String data = Base64.encode("\000" + username + "\000" + password);
|
||||||
try {
|
try {
|
||||||
executeSimpleCommand("AUTH PLAIN " + data, true);
|
executeSimpleCommand("AUTH PLAIN " + data, true);
|
||||||
} catch (NegativeSmtpReplyException exception) {
|
} catch (NegativeSmtpReplyException exception) {
|
||||||
@ -756,7 +757,7 @@ public class SmtpTransport extends Transport {
|
|||||||
private void saslAuthExternal(String username) throws MessagingException, IOException {
|
private void saslAuthExternal(String username) throws MessagingException, IOException {
|
||||||
executeSimpleCommand(
|
executeSimpleCommand(
|
||||||
String.format("AUTH EXTERNAL %s",
|
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 android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.Account;
|
import com.fsck.k9.mail.K9MailLib;
|
||||||
import com.fsck.k9.K9;
|
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.ServerSettings;
|
import com.fsck.k9.mail.ServerSettings;
|
||||||
import com.fsck.k9.mail.Transport;
|
import com.fsck.k9.mail.Transport;
|
||||||
|
import com.fsck.k9.mail.store.StoreConfig;
|
||||||
import com.fsck.k9.mail.store.WebDavStore;
|
import com.fsck.k9.mail.store.WebDavStore;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
|
||||||
|
|
||||||
public class WebDavTransport extends Transport {
|
public class WebDavTransport extends Transport {
|
||||||
public static final String TRANSPORT_TYPE = WebDavStore.STORE_TYPE;
|
public static final String TRANSPORT_TYPE = WebDavStore.STORE_TYPE;
|
||||||
|
|
||||||
@ -43,21 +45,17 @@ public class WebDavTransport extends Transport {
|
|||||||
|
|
||||||
private WebDavStore store;
|
private WebDavStore store;
|
||||||
|
|
||||||
public WebDavTransport(Account account) throws MessagingException {
|
public WebDavTransport(StoreConfig storeConfig) throws MessagingException {
|
||||||
if (account.getRemoteStore() instanceof WebDavStore) {
|
store = new WebDavStore(storeConfig);
|
||||||
store = (WebDavStore) account.getRemoteStore();
|
|
||||||
} else {
|
|
||||||
store = new WebDavStore(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (K9.DEBUG)
|
if (K9MailLib.isDebug())
|
||||||
Log.d(K9.LOG_TAG, ">>> New WebDavTransport creation complete");
|
Log.d(LOG_TAG, ">>> New WebDavTransport creation complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void open() throws MessagingException {
|
public void open() throws MessagingException {
|
||||||
if (K9.DEBUG)
|
if (K9MailLib.isDebug())
|
||||||
Log.d(K9.LOG_TAG, ">>> open called on WebDavTransport ");
|
Log.d(LOG_TAG, ">>> open called on WebDavTransport ");
|
||||||
|
|
||||||
store.getHttpClient();
|
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.IOException;
|
||||||
import java.io.InputStream;
|
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.IOException;
|
||||||
import java.io.InputStream;
|
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.ByteArrayInputStream;
|
||||||
import java.io.FileNotFoundException;
|
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.Body;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
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.IOException;
|
||||||
import java.io.OutputStream;
|
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.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@ -28,11 +28,11 @@ import android.database.sqlite.SQLiteDatabase;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.fsck.k9.Account;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.Account.MessageFormat;
|
import com.fsck.k9.Account.MessageFormat;
|
||||||
import com.fsck.k9.activity.Search;
|
import com.fsck.k9.activity.Search;
|
||||||
import com.fsck.k9.controller.MessageRemovalListener;
|
import com.fsck.k9.mail.MessageRetrievalListener;
|
||||||
import com.fsck.k9.controller.MessageRetrievalListener;
|
|
||||||
import com.fsck.k9.helper.HtmlConverter;
|
import com.fsck.k9.helper.HtmlConverter;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
import com.fsck.k9.mail.Address;
|
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.MimeMultipart;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility.ViewableContainer;
|
import com.fsck.k9.mailstore.LockableDatabase.DbCallback;
|
||||||
import com.fsck.k9.mail.store.StorageManager;
|
import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
|
||||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
|
||||||
import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback;
|
|
||||||
import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException;
|
|
||||||
import com.fsck.k9.provider.AttachmentProvider;
|
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;
|
private static final long serialVersionUID = -1973296520918624767L;
|
||||||
|
|
||||||
@ -80,22 +78,19 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
private Integer mLastUid = null;
|
private Integer mLastUid = null;
|
||||||
|
|
||||||
public LocalFolder(LocalStore localStore, String name) {
|
public LocalFolder(LocalStore localStore, String name) {
|
||||||
super(localStore.getAccount());
|
super();
|
||||||
this.localStore = localStore;
|
this.localStore = localStore;
|
||||||
this.mName = name;
|
this.mName = name;
|
||||||
|
|
||||||
if (this.localStore.getAccount().getInboxFolderName().equals(getName())) {
|
if (getAccount().getInboxFolderName().equals(getName())) {
|
||||||
|
|
||||||
mSyncClass = FolderClass.FIRST_CLASS;
|
mSyncClass = FolderClass.FIRST_CLASS;
|
||||||
mPushClass = FolderClass.FIRST_CLASS;
|
mPushClass = FolderClass.FIRST_CLASS;
|
||||||
mInTopGroup = true;
|
mInTopGroup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalFolder(LocalStore localStore, long id) {
|
public LocalFolder(LocalStore localStore, long id) {
|
||||||
super(localStore.getAccount());
|
super();
|
||||||
this.localStore = localStore;
|
this.localStore = localStore;
|
||||||
this.mFolderId = id;
|
this.mFolderId = id;
|
||||||
}
|
}
|
||||||
@ -104,6 +99,23 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
return mFolderId;
|
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
|
@Override
|
||||||
public void open(final int mode) throws MessagingException {
|
public void open(final int mode) throws MessagingException {
|
||||||
|
|
||||||
@ -216,7 +228,7 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean create(FolderType type) throws MessagingException {
|
public boolean create(FolderType type) throws MessagingException {
|
||||||
return create(type, mAccount.getDisplayCount());
|
return create(type, getAccount().getDisplayCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -517,25 +529,25 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
String id = getPrefId();
|
String id = getPrefId();
|
||||||
|
|
||||||
// there can be a lot of folders. For the defaults, let's not save prefs, saving space, except for INBOX
|
// 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");
|
editor.remove(id + ".displayMode");
|
||||||
} else {
|
} else {
|
||||||
editor.putString(id + ".displayMode", mDisplayClass.name());
|
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");
|
editor.remove(id + ".syncMode");
|
||||||
} else {
|
} else {
|
||||||
editor.putString(id + ".syncMode", mSyncClass.name());
|
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");
|
editor.remove(id + ".notifyMode");
|
||||||
} else {
|
} else {
|
||||||
editor.putString(id + ".notifyMode", mNotifyClass.name());
|
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");
|
editor.remove(id + ".pushMode");
|
||||||
} else {
|
} else {
|
||||||
editor.putString(id + ".pushMode", mPushClass.name());
|
editor.putString(id + ".pushMode", mPushClass.name());
|
||||||
@ -597,7 +609,7 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 {
|
throws MessagingException {
|
||||||
try {
|
try {
|
||||||
this.localStore.database.execute(false, new DbCallback<Void>() {
|
this.localStore.database.execute(false, new DbCallback<Void>() {
|
||||||
@ -614,7 +626,7 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
try {
|
try {
|
||||||
cursor = db.rawQuery("SELECT html_content, text_content, mime_type FROM messages "
|
cursor = db.rawQuery("SELECT html_content, text_content, mime_type FROM messages "
|
||||||
+ "WHERE id = ?",
|
+ "WHERE id = ?",
|
||||||
new String[] { Long.toString(localMessage.mId) });
|
new String[] { Long.toString(localMessage.getId()) });
|
||||||
cursor.moveToNext();
|
cursor.moveToNext();
|
||||||
String htmlContent = cursor.getString(0);
|
String htmlContent = cursor.getString(0);
|
||||||
String textContent = cursor.getString(1);
|
String textContent = cursor.getString(1);
|
||||||
@ -629,7 +641,7 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
mp.addBodyPart(bp);
|
mp.addBodyPart(bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAccount.getMessageFormat() != MessageFormat.TEXT) {
|
if (getAccount().getMessageFormat() != MessageFormat.TEXT) {
|
||||||
if (htmlContent != null) {
|
if (htmlContent != null) {
|
||||||
TextBody body = new TextBody(htmlContent);
|
TextBody body = new TextBody(htmlContent);
|
||||||
MimeBodyPart bp = new MimeBodyPart(body, "text/html");
|
MimeBodyPart bp = new MimeBodyPart(body, "text/html");
|
||||||
@ -700,7 +712,7 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
"content_disposition"
|
"content_disposition"
|
||||||
},
|
},
|
||||||
"message_id = ?",
|
"message_id = ?",
|
||||||
new String[] { Long.toString(localMessage.mId) },
|
new String[] { Long.toString(localMessage.getId()) },
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null);
|
null);
|
||||||
@ -813,10 +825,10 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
* The messages whose headers should be loaded.
|
* The messages whose headers should be loaded.
|
||||||
* @throws UnavailableStorageException
|
* @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>() {
|
this.localStore.database.execute(false, new DbCallback<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, MessagingException {
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
if (messages.isEmpty()) {
|
if (messages.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
@ -1123,14 +1135,14 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
* @return The local version of the message. Never <code>null</code>.
|
* @return The local version of the message. Never <code>null</code>.
|
||||||
* @throws MessagingException
|
* @throws MessagingException
|
||||||
*/
|
*/
|
||||||
public Message storeSmallMessage(final Message message, final Runnable runnable) throws MessagingException {
|
public LocalMessage storeSmallMessage(final Message message, final Runnable runnable) throws MessagingException {
|
||||||
return this.localStore.database.execute(true, new DbCallback<Message>() {
|
return this.localStore.database.execute(true, new DbCallback<LocalMessage>() {
|
||||||
@Override
|
@Override
|
||||||
public Message doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
public LocalMessage doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
||||||
try {
|
try {
|
||||||
appendMessages(Collections.singletonList(message));
|
appendMessages(Collections.singletonList(message));
|
||||||
final String uid = message.getUid();
|
final String uid = message.getUid();
|
||||||
final Message result = getMessage(uid);
|
final LocalMessage result = getMessage(uid);
|
||||||
runnable.run();
|
runnable.run();
|
||||||
// Set a flag indicating this message has now be fully downloaded
|
// Set a flag indicating this message has now be fully downloaded
|
||||||
result.setFlag(Flag.X_DOWNLOADED_FULL, true);
|
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
|
// draft messages because this will cause the values stored in
|
||||||
// the identity header to be wrong.
|
// the identity header to be wrong.
|
||||||
ViewableContainer container =
|
ViewableContainer container =
|
||||||
MimeUtility.extractPartsFromDraft(message);
|
LocalMessageExtractor.extractPartsFromDraft(message);
|
||||||
|
|
||||||
text = container.text;
|
text = container.text;
|
||||||
html = container.html;
|
html = container.html;
|
||||||
attachments = container.attachments;
|
attachments = container.attachments;
|
||||||
} else {
|
} else {
|
||||||
ViewableContainer container =
|
ViewableContainer container =
|
||||||
MimeUtility.extractTextAndAttachments(LocalFolder.this.localStore.mApplication, message);
|
LocalMessageExtractor.extractTextAndAttachments(LocalFolder.this.localStore.mApplication, message);
|
||||||
|
|
||||||
attachments = container.attachments;
|
attachments = container.attachments;
|
||||||
text = container.text;
|
text = container.text;
|
||||||
@ -1400,7 +1412,7 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
message.buildMimeRepresentation();
|
message.buildMimeRepresentation();
|
||||||
|
|
||||||
ViewableContainer container =
|
ViewableContainer container =
|
||||||
MimeUtility.extractTextAndAttachments(LocalFolder.this.localStore.mApplication, message);
|
LocalMessageExtractor.extractTextAndAttachments(LocalFolder.this.localStore.mApplication, message);
|
||||||
|
|
||||||
List<Part> attachments = container.attachments;
|
List<Part> attachments = container.attachments;
|
||||||
String text = container.text;
|
String text = container.text;
|
||||||
@ -1439,12 +1451,12 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
message.isSet(Flag.FLAGGED) ? 1 : 0,
|
message.isSet(Flag.FLAGGED) ? 1 : 0,
|
||||||
message.isSet(Flag.ANSWERED) ? 1 : 0,
|
message.isSet(Flag.ANSWERED) ? 1 : 0,
|
||||||
message.isSet(Flag.FORWARDED) ? 1 : 0,
|
message.isSet(Flag.FORWARDED) ? 1 : 0,
|
||||||
message.mId
|
message.getId()
|
||||||
});
|
});
|
||||||
|
|
||||||
for (int i = 0, count = attachments.size(); i < count; i++) {
|
for (int i = 0, count = attachments.size(); i < count; i++) {
|
||||||
Part attachment = attachments.get(i);
|
Part attachment = attachments.get(i);
|
||||||
saveAttachment(message.mId, attachment, false);
|
saveAttachment(message.getId(), attachment, false);
|
||||||
}
|
}
|
||||||
saveHeaders(message.getId(), message);
|
saveHeaders(message.getId(), message);
|
||||||
} catch (Exception e) {
|
} 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 {
|
private void saveHeaders(final long id, final MimeMessage message) throws MessagingException {
|
||||||
this.localStore.database.execute(true, new DbCallback<Void>() {
|
this.localStore.database.execute(true, new DbCallback<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, MessagingException {
|
||||||
|
|
||||||
deleteHeaders(id);
|
deleteHeaders(id);
|
||||||
for (String name : message.getHeaderNames()) {
|
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>() {
|
this.localStore.database.execute(false, new DbCallback<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
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));
|
File attachmentFile = new File(attachmentDirectory, Long.toString(attachmentId));
|
||||||
tempAttachmentFile.renameTo(attachmentFile);
|
tempAttachmentFile.renameTo(attachmentFile);
|
||||||
contentUri = AttachmentProvider.getAttachmentUri(
|
contentUri = AttachmentProvider.getAttachmentUri(
|
||||||
mAccount,
|
getAccount(),
|
||||||
attachmentId);
|
attachmentId);
|
||||||
if (MimeUtil.isMessage(attachment.getMimeType())) {
|
if (MimeUtil.isMessage(attachment.getMimeType())) {
|
||||||
attachment.setBody(new LocalAttachmentMessageBody(
|
attachment.setBody(new LocalAttachmentMessageBody(
|
||||||
@ -1712,7 +1724,7 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
@Override
|
@Override
|
||||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
||||||
db.update("messages", cv, "id = ?", new String[]
|
db.update("messages", cv, "id = ?", new String[]
|
||||||
{ Long.toString(message.mId) });
|
{ Long.toString(message.getId()) });
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1829,7 +1841,7 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
setPushState(null);
|
setPushState(null);
|
||||||
setLastPush(0);
|
setLastPush(0);
|
||||||
setLastChecked(0);
|
setLastChecked(0);
|
||||||
setVisibleLimit(mAccount.getDisplayCount());
|
setVisibleLimit(getAccount().getDisplayCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1878,7 +1890,7 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
||||||
Cursor attachmentsCursor = null;
|
Cursor attachmentsCursor = null;
|
||||||
try {
|
try {
|
||||||
String accountUuid = mAccount.getUuid();
|
String accountUuid = getAccountUuid();
|
||||||
Context context = LocalFolder.this.localStore.mApplication;
|
Context context = LocalFolder.this.localStore.mApplication;
|
||||||
|
|
||||||
// Get attachment IDs
|
// 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
|
// Append the first message ID from the "In-Reply-To" header line
|
||||||
String[] inReplyToArray = message.getHeader("In-Reply-To");
|
String[] inReplyToArray = message.getHeader("In-Reply-To");
|
||||||
String inReplyTo = null;
|
String inReplyTo;
|
||||||
if (inReplyToArray != null && inReplyToArray.length > 0) {
|
if (inReplyToArray != null && inReplyToArray.length > 0) {
|
||||||
inReplyTo = Utility.extractMessageId(inReplyToArray[0]);
|
inReplyTo = Utility.extractMessageId(inReplyToArray[0]);
|
||||||
if (inReplyTo != null) {
|
if (inReplyTo != null) {
|
||||||
@ -2194,4 +2206,8 @@ public class LocalFolder extends Folder implements Serializable {
|
|||||||
throw(MessagingException) e.getCause();
|
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.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
@ -12,23 +12,25 @@ import android.database.Cursor;
|
|||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.fsck.k9.Account;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
|
import com.fsck.k9.activity.MessageReference;
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Part;
|
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.MimeMessage;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
import com.fsck.k9.mailstore.LockableDatabase.DbCallback;
|
||||||
import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback;
|
import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
|
||||||
import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException;
|
|
||||||
|
|
||||||
public class LocalMessage extends MimeMessage {
|
public class LocalMessage extends MimeMessage {
|
||||||
|
protected MessageReference mReference;
|
||||||
private final LocalStore localStore;
|
private final LocalStore localStore;
|
||||||
|
|
||||||
long mId;
|
private long mId;
|
||||||
private int mAttachmentCount;
|
private int mAttachmentCount;
|
||||||
private String mSubject;
|
private String mSubject;
|
||||||
|
|
||||||
@ -50,8 +52,7 @@ public class LocalMessage extends MimeMessage {
|
|||||||
this.mFolder = folder;
|
this.mFolder = folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
void populateFromGetMessageCursor(Cursor cursor)
|
void populateFromGetMessageCursor(Cursor cursor) throws MessagingException {
|
||||||
throws MessagingException {
|
|
||||||
final String subject = cursor.getString(0);
|
final String subject = cursor.getString(0);
|
||||||
this.setSubject(subject == null ? "" : subject);
|
this.setSubject(subject == null ? "" : subject);
|
||||||
|
|
||||||
@ -129,7 +130,7 @@ public class LocalMessage extends MimeMessage {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We successfully found an HTML part; do the necessary character set decoding.
|
// We successfully found an HTML part; do the necessary character set decoding.
|
||||||
text = MimeUtility.getTextFromPart(part);
|
text = MessageExtractor.getTextFromPart(this);
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
@ -156,7 +157,7 @@ public class LocalMessage extends MimeMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
super.setReplyTo(mReplyTo);
|
super.setReplyTo(mReplyTo);
|
||||||
super.setSentDate(this.getSentDate());
|
super.setSentDate(this.getSentDate(), K9.hideTimeZone());
|
||||||
super.setRecipients(RecipientType.TO, mTo);
|
super.setRecipients(RecipientType.TO, mTo);
|
||||||
super.setRecipients(RecipientType.CC, mCc);
|
super.setRecipients(RecipientType.CC, mCc);
|
||||||
super.setRecipients(RecipientType.BCC, mBcc);
|
super.setRecipients(RecipientType.BCC, mBcc);
|
||||||
@ -189,6 +190,12 @@ public class LocalMessage extends MimeMessage {
|
|||||||
mMessageDirty = true;
|
mMessageDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUid(String uid) {
|
||||||
|
super.setUid(uid);
|
||||||
|
this.mReference = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasAttachments() {
|
public boolean hasAttachments() {
|
||||||
return (mAttachmentCount > 0);
|
return (mAttachmentCount > 0);
|
||||||
@ -492,44 +499,44 @@ public class LocalMessage extends MimeMessage {
|
|||||||
db.delete("threads", "message_id = ?", idArg);
|
db.delete("threads", "message_id = ?", idArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadHeaders() throws UnavailableStorageException {
|
private void loadHeaders() throws MessagingException {
|
||||||
List<LocalMessage> messages = new ArrayList<LocalMessage>();
|
List<LocalMessage> messages = new ArrayList<LocalMessage>();
|
||||||
messages.add(this);
|
messages.add(this);
|
||||||
mHeadersLoaded = true; // set true before calling populate headers to stop recursion
|
mHeadersLoaded = true; // set true before calling populate headers to stop recursion
|
||||||
((LocalFolder) mFolder).populateHeaders(messages);
|
getFolder().populateHeaders(messages);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addHeader(String name, String value) throws UnavailableStorageException {
|
public void addHeader(String name, String value) throws MessagingException {
|
||||||
if (!mHeadersLoaded)
|
if (!mHeadersLoaded)
|
||||||
loadHeaders();
|
loadHeaders();
|
||||||
super.addHeader(name, value);
|
super.addHeader(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setHeader(String name, String value) throws UnavailableStorageException {
|
public void setHeader(String name, String value) throws MessagingException {
|
||||||
if (!mHeadersLoaded)
|
if (!mHeadersLoaded)
|
||||||
loadHeaders();
|
loadHeaders();
|
||||||
super.setHeader(name, value);
|
super.setHeader(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getHeader(String name) throws UnavailableStorageException {
|
public String[] getHeader(String name) throws MessagingException {
|
||||||
if (!mHeadersLoaded)
|
if (!mHeadersLoaded)
|
||||||
loadHeaders();
|
loadHeaders();
|
||||||
return super.getHeader(name);
|
return super.getHeader(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeHeader(String name) throws UnavailableStorageException {
|
public void removeHeader(String name) throws MessagingException {
|
||||||
if (!mHeadersLoaded)
|
if (!mHeadersLoaded)
|
||||||
loadHeaders();
|
loadHeaders();
|
||||||
super.removeHeader(name);
|
super.removeHeader(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getHeaderNames() throws UnavailableStorageException {
|
public Set<String> getHeaderNames() throws MessagingException {
|
||||||
if (!mHeadersLoaded)
|
if (!mHeadersLoaded)
|
||||||
loadHeaders();
|
loadHeaders();
|
||||||
return super.getHeaderNames();
|
return super.getHeaderNames();
|
||||||
@ -557,4 +564,56 @@ public class LocalMessage extends MimeMessage {
|
|||||||
public long getRootId() {
|
public long getRootId() {
|
||||||
return mRootId;
|
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;
|
package com.fsck.k9.mailstore;
|
||||||
|
|
||||||
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 android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
@ -21,22 +11,20 @@ import android.database.sqlite.SQLiteDatabase;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.Account;
|
import com.fsck.k9.Account;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.Preferences;
|
import com.fsck.k9.Preferences;
|
||||||
import com.fsck.k9.controller.MessageRetrievalListener;
|
|
||||||
import com.fsck.k9.helper.UrlEncodingHelper;
|
import com.fsck.k9.helper.UrlEncodingHelper;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
|
import com.fsck.k9.mail.MessageRetrievalListener;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Store;
|
import com.fsck.k9.mail.Store;
|
||||||
import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback;
|
import com.fsck.k9.mailstore.StorageManager.StorageProvider;
|
||||||
import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException;
|
import com.fsck.k9.mail.store.StoreConfig;
|
||||||
import com.fsck.k9.mail.store.StorageManager;
|
import com.fsck.k9.mailstore.LockableDatabase.DbCallback;
|
||||||
import com.fsck.k9.mail.store.StorageManager.StorageProvider;
|
import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
|
||||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
|
||||||
import com.fsck.k9.provider.EmailProvider;
|
import com.fsck.k9.provider.EmailProvider;
|
||||||
import com.fsck.k9.provider.EmailProvider.MessageColumns;
|
import com.fsck.k9.provider.EmailProvider.MessageColumns;
|
||||||
import com.fsck.k9.search.LocalSearch;
|
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.SearchSpecification.Searchfield;
|
||||||
import com.fsck.k9.search.SqlQueryBuilder;
|
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>
|
* <pre>
|
||||||
* Implements a SQLite database backed local store for Messages.
|
* 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 String[] EMPTY_STRING_ARRAY = new String[0];
|
||||||
static final byte[] EMPTY_BYTE_ARRAY = new byte[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
|
* a String containing the columns getMessages expects to work with
|
||||||
* in the correct order.
|
* in the correct order.
|
||||||
@ -138,6 +150,7 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
LockableDatabase database;
|
LockableDatabase database;
|
||||||
|
|
||||||
private ContentResolver mContentResolver;
|
private ContentResolver mContentResolver;
|
||||||
|
private final Account mAccount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* local://localhost/path/to/database/uuid.db
|
* 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)}
|
* @throws UnavailableStorageException if not {@link StorageProvider#isReady(Context)}
|
||||||
*/
|
*/
|
||||||
public LocalStore(final Account account, final Application application) throws MessagingException {
|
public LocalStore(final Account account, final Application application) throws MessagingException {
|
||||||
super(account);
|
mAccount = account;
|
||||||
database = new LockableDatabase(application, account.getUuid(), new StoreSchemaDefinition(this));
|
database = new LockableDatabase(application, account.getUuid(), new StoreSchemaDefinition(this));
|
||||||
|
|
||||||
mApplication = application;
|
mApplication = application;
|
||||||
@ -158,15 +171,66 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
database.open();
|
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 {
|
public void switchLocalStorage(final String newStorageProviderId) throws MessagingException {
|
||||||
database.switchProvider(newStorageProviderId);
|
database.switchProvider(newStorageProviderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Account getAccount() {
|
||||||
|
return mAccount;
|
||||||
|
}
|
||||||
|
|
||||||
protected SharedPreferences getPreferences() {
|
protected SharedPreferences getPreferences() {
|
||||||
return Preferences.getPreferences(mApplication).getPreferences();
|
return Preferences.getPreferences(mApplication).getPreferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getSize() throws UnavailableStorageException {
|
public long getSize() throws MessagingException {
|
||||||
|
|
||||||
final StorageManager storageManager = StorageManager.getInstance(mApplication);
|
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();
|
final ContentValues cv = new ContentValues();
|
||||||
cv.put("visible_limit", Integer.toString(visibleLimit));
|
cv.put("visible_limit", Integer.toString(visibleLimit));
|
||||||
database.execute(false, new DbCallback<Void>() {
|
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>>() {
|
return database.execute(false, new DbCallback<List<PendingCommand>>() {
|
||||||
@Override
|
@Override
|
||||||
public List<PendingCommand> doDbWork(final SQLiteDatabase db) throws WrappedException {
|
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++) {
|
for (int i = 0; i < command.arguments.length; i++) {
|
||||||
command.arguments[i] = UrlEncodingHelper.encodeUtf8(command.arguments[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>() {
|
database.execute(false, new DbCallback<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
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>() {
|
database.execute(false, new DbCallback<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
||||||
@ -612,7 +676,7 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
return searchForMessages(null, search);
|
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>() {
|
return database.execute(false, new DbCallback<AttachmentInfo>() {
|
||||||
@Override
|
@Override
|
||||||
public AttachmentInfo doDbWork(final SQLiteDatabase db) throws WrappedException {
|
public AttachmentInfo doDbWork(final SQLiteDatabase db) throws WrappedException {
|
||||||
@ -653,7 +717,7 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
public String type;
|
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>() {
|
database.execute(true, new DbCallback<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
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;
|
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.io.File;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
@ -16,8 +16,6 @@ import android.util.Log;
|
|||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.helper.FileHelper;
|
import com.fsck.k9.helper.FileHelper;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.store.StorageManager;
|
|
||||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
|
||||||
|
|
||||||
public class LockableDatabase {
|
public class LockableDatabase {
|
||||||
|
|
||||||
@ -35,9 +33,10 @@ public class LockableDatabase {
|
|||||||
* <code>null</code>.
|
* <code>null</code>.
|
||||||
* @return Any relevant data. Can be <code>null</code>.
|
* @return Any relevant data. Can be <code>null</code>.
|
||||||
* @throws WrappedException
|
* @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 {
|
public static interface SchemaDefinition {
|
||||||
@ -272,7 +271,7 @@ public class LockableDatabase {
|
|||||||
* @return Whatever {@link DbCallback#doDbWork(SQLiteDatabase)} returns.
|
* @return Whatever {@link DbCallback#doDbWork(SQLiteDatabase)} returns.
|
||||||
* @throws UnavailableStorageException
|
* @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();
|
lockRead();
|
||||||
final boolean doTransaction = transactional && inTransaction.get() == null;
|
final boolean doTransaction = transactional && inTransaction.get() == null;
|
||||||
try {
|
try {
|
@ -1,4 +1,4 @@
|
|||||||
package com.fsck.k9.controller;
|
package com.fsck.k9.mailstore;
|
||||||
|
|
||||||
import com.fsck.k9.mail.Message;
|
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.File;
|
||||||
import java.io.IOException;
|
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.ArrayList;
|
||||||
import java.util.List;
|
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.ByteArrayInputStream;
|
||||||
import java.io.File;
|
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.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
@ -1,4 +1,4 @@
|
|||||||
package com.fsck.k9.mail.store.local;
|
package com.fsck.k9.mailstore;
|
||||||
|
|
||||||
class ThreadInfo {
|
class ThreadInfo {
|
||||||
public final long threadId;
|
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;
|
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.K9;
|
||||||
import com.fsck.k9.R;
|
import com.fsck.k9.R;
|
||||||
import com.fsck.k9.Account.FolderMode;
|
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.*;
|
import com.fsck.k9.preferences.Settings.*;
|
||||||
|
|
||||||
public class AccountSettings {
|
public class AccountSettings {
|
||||||
|
@ -25,9 +25,9 @@ import android.util.Xml;
|
|||||||
import com.fsck.k9.Account;
|
import com.fsck.k9.Account;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.Preferences;
|
import com.fsck.k9.Preferences;
|
||||||
import com.fsck.k9.mail.Store;
|
|
||||||
import com.fsck.k9.mail.ServerSettings;
|
import com.fsck.k9.mail.ServerSettings;
|
||||||
import com.fsck.k9.mail.Transport;
|
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.InvalidSettingValueException;
|
||||||
import com.fsck.k9.preferences.Settings.SettingsDescription;
|
import com.fsck.k9.preferences.Settings.SettingsDescription;
|
||||||
|
|
||||||
@ -223,7 +223,7 @@ public class SettingsExporter {
|
|||||||
|
|
||||||
|
|
||||||
// Write incoming server settings
|
// Write incoming server settings
|
||||||
ServerSettings incoming = Store.decodeStoreUri(account.getStoreUri());
|
ServerSettings incoming = RemoteStore.decodeStoreUri(account.getStoreUri());
|
||||||
serializer.startTag(null, INCOMING_SERVER_ELEMENT);
|
serializer.startTag(null, INCOMING_SERVER_ELEMENT);
|
||||||
serializer.attribute(null, TYPE_ATTRIBUTE, incoming.type);
|
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.Identity;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.Preferences;
|
import com.fsck.k9.Preferences;
|
||||||
import com.fsck.k9.helper.Utility;
|
|
||||||
import com.fsck.k9.mail.AuthType;
|
import com.fsck.k9.mail.AuthType;
|
||||||
import com.fsck.k9.mail.ConnectionSecurity;
|
import com.fsck.k9.mail.ConnectionSecurity;
|
||||||
import com.fsck.k9.mail.ServerSettings;
|
import com.fsck.k9.mail.ServerSettings;
|
||||||
import com.fsck.k9.mail.Store;
|
|
||||||
import com.fsck.k9.mail.Transport;
|
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.mail.store.WebDavStore;
|
||||||
import com.fsck.k9.preferences.Settings.InvalidSettingValueException;
|
import com.fsck.k9.preferences.Settings.InvalidSettingValueException;
|
||||||
|
|
||||||
@ -376,8 +376,8 @@ public class SettingsImporter {
|
|||||||
|
|
||||||
// Write incoming server settings (storeUri)
|
// Write incoming server settings (storeUri)
|
||||||
ServerSettings incoming = new ImportedServerSettings(account.incoming);
|
ServerSettings incoming = new ImportedServerSettings(account.incoming);
|
||||||
String storeUri = Store.createStoreUri(incoming);
|
String storeUri = RemoteStore.createStoreUri(incoming);
|
||||||
putString(editor, accountKeyPrefix + Account.STORE_URI_KEY, Utility.base64Encode(storeUri));
|
putString(editor, accountKeyPrefix + Account.STORE_URI_KEY, Base64.encode(storeUri));
|
||||||
|
|
||||||
// Mark account as disabled if the AuthType isn't EXTERNAL and the
|
// Mark account as disabled if the AuthType isn't EXTERNAL and the
|
||||||
// settings file didn't contain a password
|
// settings file didn't contain a password
|
||||||
@ -393,7 +393,7 @@ public class SettingsImporter {
|
|||||||
// Write outgoing server settings (transportUri)
|
// Write outgoing server settings (transportUri)
|
||||||
ServerSettings outgoing = new ImportedServerSettings(account.outgoing);
|
ServerSettings outgoing = new ImportedServerSettings(account.outgoing);
|
||||||
String transportUri = Transport.createTransportUri(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
|
* 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.K9;
|
||||||
import com.fsck.k9.helper.UrlEncodingHelper;
|
import com.fsck.k9.helper.UrlEncodingHelper;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
|
import com.fsck.k9.mail.filter.Base64;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -54,8 +54,8 @@ public class Storage implements SharedPreferences {
|
|||||||
String[] uuids = accountUuids.split(",");
|
String[] uuids = accountUuids.split(",");
|
||||||
for (String uuid : uuids) {
|
for (String uuid : uuids) {
|
||||||
try {
|
try {
|
||||||
String storeUriStr = Utility.base64Decode(readValue(mDb, uuid + ".storeUri"));
|
String storeUriStr = Base64.decode(readValue(mDb, uuid + ".storeUri"));
|
||||||
String transportUriStr = Utility.base64Decode(readValue(mDb, uuid + ".transportUri"));
|
String transportUriStr = Base64.decode(readValue(mDb, uuid + ".transportUri"));
|
||||||
|
|
||||||
URI uri = new URI(transportUriStr);
|
URI uri = new URI(transportUriStr);
|
||||||
String newUserInfo = null;
|
String newUserInfo = null;
|
||||||
@ -77,7 +77,7 @@ public class Storage implements SharedPreferences {
|
|||||||
|
|
||||||
if (newUserInfo != null) {
|
if (newUserInfo != null) {
|
||||||
URI newUri = new URI(uri.getScheme(), newUserInfo, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment());
|
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);
|
writeValue(mDb, uuid + ".transportUri", newTransportUriStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ public class Storage implements SharedPreferences {
|
|||||||
|
|
||||||
if (newUserInfo != null) {
|
if (newUserInfo != null) {
|
||||||
URI newUri = new URI(uri.getScheme(), newUserInfo, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment());
|
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);
|
writeValue(mDb, uuid + ".storeUri", newStoreUriStr);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -15,9 +15,9 @@ import com.fsck.k9.K9;
|
|||||||
import com.fsck.k9.Preferences;
|
import com.fsck.k9.Preferences;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.store.local.LocalStore;
|
import com.fsck.k9.mailstore.LocalStore;
|
||||||
import com.fsck.k9.mail.store.local.LocalStore.AttachmentInfo;
|
import com.fsck.k9.mailstore.LocalStore.AttachmentInfo;
|
||||||
import com.fsck.k9.mail.store.StorageManager;
|
import com.fsck.k9.mailstore.StorageManager;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -215,7 +215,7 @@ public class AttachmentProvider extends ContentProvider {
|
|||||||
final AttachmentInfo attachmentInfo;
|
final AttachmentInfo attachmentInfo;
|
||||||
try {
|
try {
|
||||||
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
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) {
|
} catch (MessagingException e) {
|
||||||
Log.e(K9.LOG_TAG, "Unable to retrieve attachment info from local store for ID: " + id, e);
|
Log.e(K9.LOG_TAG, "Unable to retrieve attachment info from local store for ID: " + id, e);
|
||||||
return null;
|
return null;
|
||||||
@ -269,7 +269,7 @@ public class AttachmentProvider extends ContentProvider {
|
|||||||
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final LocalStore localStore = LocalStore.getLocalInstance(account, K9.app);
|
final LocalStore localStore = LocalStore.getInstance(account, K9.app);
|
||||||
|
|
||||||
AttachmentInfo attachmentInfo = localStore.getAttachmentInfo(id);
|
AttachmentInfo attachmentInfo = localStore.getAttachmentInfo(id);
|
||||||
if (FORMAT_VIEW.equals(format) && mimeType != null) {
|
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.cache.EmailProviderCacheCursor;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.store.local.LockableDatabase;
|
import com.fsck.k9.mailstore.LockableDatabase;
|
||||||
import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback;
|
import com.fsck.k9.mailstore.LockableDatabase.DbCallback;
|
||||||
import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException;
|
import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
|
||||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
import com.fsck.k9.mailstore.UnavailableStorageException;
|
||||||
import com.fsck.k9.mail.store.local.LocalStore;
|
import com.fsck.k9.mailstore.LocalStore;
|
||||||
import com.fsck.k9.search.SqlQueryBuilder;
|
import com.fsck.k9.search.SqlQueryBuilder;
|
||||||
|
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
@ -357,6 +357,8 @@ public class EmailProvider extends ContentProvider {
|
|||||||
});
|
});
|
||||||
} catch (UnavailableStorageException e) {
|
} catch (UnavailableStorageException e) {
|
||||||
throw new RuntimeException("Storage not available", 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) {
|
} catch (UnavailableStorageException e) {
|
||||||
throw new RuntimeException("Storage not available", 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) {
|
} catch (UnavailableStorageException e) {
|
||||||
throw new RuntimeException("Storage not available", e);
|
throw new RuntimeException("Storage not available", e);
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
throw new RuntimeException("messaging exception", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Cursor getAccountStats(String accountUuid, String[] columns,
|
private Cursor getAccountStats(String accountUuid, String[] columns,
|
||||||
@ -594,7 +601,10 @@ public class EmailProvider extends ContentProvider {
|
|||||||
});
|
});
|
||||||
} catch (UnavailableStorageException e) {
|
} catch (UnavailableStorageException e) {
|
||||||
throw new RuntimeException("Storage not available", e);
|
throw new RuntimeException("Storage not available", e);
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
throw new RuntimeException("messaging exception", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Account getAccount(String accountUuid) {
|
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.Folder;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.store.local.LocalMessage;
|
import com.fsck.k9.mailstore.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.local.LocalStore;
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
|
import com.fsck.k9.mailstore.LocalStore;
|
||||||
import com.fsck.k9.search.SearchAccount;
|
import com.fsck.k9.search.SearchAccount;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
@ -200,9 +201,9 @@ public class MessageProvider extends ContentProvider {
|
|||||||
public static class DeleteUriExtractor implements FieldExtractor<MessageInfoHolder, String> {
|
public static class DeleteUriExtractor implements FieldExtractor<MessageInfoHolder, String> {
|
||||||
@Override
|
@Override
|
||||||
public String getField(final MessageInfoHolder source) {
|
public String getField(final MessageInfoHolder source) {
|
||||||
final Message message = source.message;
|
final LocalMessage message = source.message;
|
||||||
return CONTENT_URI + "/delete_message/"
|
return CONTENT_URI + "/delete_message/"
|
||||||
+ message.getFolder().getAccount().getAccountNumber() + "/"
|
+ message.getAccount().getAccountNumber() + "/"
|
||||||
+ message.getFolder().getName() + "/" + message.getUid();
|
+ message.getFolder().getName() + "/" + message.getUid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,21 +222,21 @@ public class MessageProvider extends ContentProvider {
|
|||||||
public static class AccountExtractor implements FieldExtractor<MessageInfoHolder, String> {
|
public static class AccountExtractor implements FieldExtractor<MessageInfoHolder, String> {
|
||||||
@Override
|
@Override
|
||||||
public String getField(final MessageInfoHolder source) {
|
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> {
|
public static class AccountColorExtractor implements FieldExtractor<MessageInfoHolder, Integer> {
|
||||||
@Override
|
@Override
|
||||||
public Integer getField(final MessageInfoHolder source) {
|
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> {
|
public static class AccountNumberExtractor implements FieldExtractor<MessageInfoHolder, Integer> {
|
||||||
@Override
|
@Override
|
||||||
public Integer getField(final MessageInfoHolder source) {
|
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
|
@Override
|
||||||
public void listLocalMessagesAddMessages(final Account account,
|
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
|
// cache fields into local variables for faster access on JVM without JIT
|
||||||
final MessageHelper helper = mMessageHelper;
|
final MessageHelper helper = mMessageHelper;
|
||||||
final List<MessageInfoHolder> holders = mHolders;
|
final List<MessageInfoHolder> holders = mHolders;
|
||||||
|
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
|
|
||||||
for (final Message message : messages) {
|
for (final LocalMessage message : messages) {
|
||||||
final MessageInfoHolder messageInfoHolder = new MessageInfoHolder();
|
final MessageInfoHolder messageInfoHolder = new MessageInfoHolder();
|
||||||
final Folder messageFolder = message.getFolder();
|
final Folder messageFolder = message.getFolder();
|
||||||
final Account messageAccount = messageFolder.getAccount();
|
|
||||||
|
|
||||||
|
final Account messageAccount = messageInfoHolder.message.getAccount();
|
||||||
helper.populate(messageInfoHolder, message, new FolderInfoHolder(context,
|
helper.populate(messageInfoHolder, message, new FolderInfoHolder(context,
|
||||||
messageFolder, messageAccount), messageAccount);
|
messageFolder, messageAccount), messageAccount);
|
||||||
|
|
||||||
@ -1038,9 +1039,9 @@ public class MessageProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get localstore parameter
|
// get localstore parameter
|
||||||
Message msg = null;
|
LocalMessage msg = null;
|
||||||
try {
|
try {
|
||||||
Folder lf = LocalStore.getLocalInstance(myAccount, K9.app).getFolder(folderName);
|
LocalFolder lf = LocalStore.getInstance(myAccount, K9.app).getFolder(folderName);
|
||||||
int msgCount = lf.getMessageCount();
|
int msgCount = lf.getMessageCount();
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "folder msg count = " + msgCount);
|
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.Account;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
import com.fsck.k9.mailstore.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.local.LocalStore;
|
import com.fsck.k9.mailstore.LocalStore;
|
||||||
import com.fsck.k9.search.SearchSpecification.Attribute;
|
import com.fsck.k9.search.SearchSpecification.Attribute;
|
||||||
import com.fsck.k9.search.SearchSpecification.SearchCondition;
|
import com.fsck.k9.search.SearchSpecification.SearchCondition;
|
||||||
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
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.activity.UpgradeDatabases;
|
||||||
import com.fsck.k9.helper.power.TracingPowerManager;
|
import com.fsck.k9.helper.power.TracingPowerManager;
|
||||||
import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
|
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.
|
* 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.MessageCompose;
|
||||||
import com.fsck.k9.activity.MessageReference;
|
import com.fsck.k9.activity.MessageReference;
|
||||||
import com.fsck.k9.controller.MessagingController;
|
import com.fsck.k9.controller.MessagingController;
|
||||||
import com.fsck.k9.helper.Utility;
|
|
||||||
import com.fsck.k9.mail.Flag;
|
import com.fsck.k9.mail.Flag;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -89,10 +88,10 @@ public class NotificationActionService extends CoreService {
|
|||||||
|
|
||||||
List<MessageReference> refs =
|
List<MessageReference> refs =
|
||||||
intent.getParcelableArrayListExtra(EXTRA_MESSAGE_LIST);
|
intent.getParcelableArrayListExtra(EXTRA_MESSAGE_LIST);
|
||||||
List<Message> messages = new ArrayList<Message>();
|
List<LocalMessage> messages = new ArrayList<LocalMessage>();
|
||||||
|
|
||||||
for (MessageReference ref : refs) {
|
for (MessageReference ref : refs) {
|
||||||
Message m = ref.restoreToLocalMessage(this);
|
LocalMessage m = ref.restoreToLocalMessage(this);
|
||||||
if (m != null) {
|
if (m != null) {
|
||||||
messages.add(m);
|
messages.add(m);
|
||||||
}
|
}
|
||||||
@ -103,10 +102,10 @@ public class NotificationActionService extends CoreService {
|
|||||||
if (K9.DEBUG)
|
if (K9.DEBUG)
|
||||||
Log.i(K9.LOG_TAG, "NotificationActionService initiating reply");
|
Log.i(K9.LOG_TAG, "NotificationActionService initiating reply");
|
||||||
|
|
||||||
MessageReference ref = (MessageReference) intent.getParcelableExtra(EXTRA_MESSAGE);
|
MessageReference ref = intent.getParcelableExtra(EXTRA_MESSAGE);
|
||||||
Message message = ref.restoreToLocalMessage(this);
|
LocalMessage message = ref.restoreToLocalMessage(this);
|
||||||
if (message != null) {
|
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);
|
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,7 +7,7 @@ import android.net.Uri;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
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.
|
* That BroadcastReceiver is only interested in UNMOUNT events.
|
||||||
|
@ -7,7 +7,7 @@ import android.net.Uri;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
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.
|
* 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.Part;
|
||||||
import com.fsck.k9.mail.internet.MimeHeader;
|
import com.fsck.k9.mail.internet.MimeHeader;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
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 com.fsck.k9.provider.AttachmentProvider;
|
||||||
import org.apache.commons.io.IOUtils;
|
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 {
|
public void populate(final Message message, final Account account) throws MessagingException {
|
||||||
final Contacts contacts = K9.showContactName() ? mContacts : null;
|
final Contacts contacts = K9.showContactName() ? mContacts : null;
|
||||||
final CharSequence from = Address.toFriendly(message.getFrom(), contacts);
|
final CharSequence from = MessageHelper.toFriendly(message.getFrom(), contacts);
|
||||||
final CharSequence to = Address.toFriendly(message.getRecipients(Message.RecipientType.TO), contacts);
|
final CharSequence to = MessageHelper.toFriendly(message.getRecipients(Message.RecipientType.TO), contacts);
|
||||||
final CharSequence cc = Address.toFriendly(message.getRecipients(Message.RecipientType.CC), contacts);
|
final CharSequence cc = MessageHelper.toFriendly(message.getRecipients(Message.RecipientType.CC), contacts);
|
||||||
|
|
||||||
Address[] fromAddrs = message.getFrom();
|
Address[] fromAddrs = message.getFrom();
|
||||||
Address[] toAddrs = message.getRecipients(Message.RecipientType.TO);
|
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