mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-23 09:52:16 -05:00
Merge remote-tracking branch 'k9mail_pgp_mime/master'
Conflicts: src/com/fsck/k9/activity/AccessibleEmailContentActivity.java
This commit is contained in:
commit
9b61fe0f0e
@ -31,9 +31,9 @@ import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Store;
|
||||
import com.fsck.k9.mail.Folder.FolderClass;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.StorageManager;
|
||||
import com.fsck.k9.mail.store.StorageManager.StorageProvider;
|
||||
import com.fsck.k9.mail.store.local.LocalStore;
|
||||
import com.fsck.k9.provider.EmailProvider;
|
||||
import com.fsck.k9.provider.EmailProvider.StatsColumns;
|
||||
import com.fsck.k9.search.ConditionsTreeNode;
|
||||
@ -181,7 +181,7 @@ public class Account implements BaseAccount {
|
||||
private boolean mPushPollOnConnect;
|
||||
private boolean mNotifySync;
|
||||
private SortType mSortType;
|
||||
private HashMap<SortType, Boolean> mSortAscending = new HashMap<SortType, Boolean>();
|
||||
private Map<SortType, Boolean> mSortAscending = new HashMap<SortType, Boolean>();
|
||||
private ShowPictures mShowPictures;
|
||||
private boolean mIsSignatureBeforeQuotedText;
|
||||
private String mExpungePolicy = EXPUNGE_IMMEDIATELY;
|
||||
@ -338,7 +338,7 @@ public class Account implements BaseAccount {
|
||||
* Pick a nice Android guidelines color if we haven't used them all yet.
|
||||
*/
|
||||
private int pickColor(Context context) {
|
||||
Account[] accounts = Preferences.getPreferences(context).getAccounts();
|
||||
List<Account> accounts = Preferences.getPreferences(context).getAccounts();
|
||||
|
||||
List<Integer> availableColors = new ArrayList<Integer>(PREDEFINED_COLORS.length);
|
||||
Collections.addAll(availableColors, PREDEFINED_COLORS);
|
||||
@ -620,8 +620,8 @@ public class Account implements BaseAccount {
|
||||
}
|
||||
|
||||
public static List<Integer> getExistingAccountNumbers(Preferences preferences) {
|
||||
Account[] accounts = preferences.getAccounts();
|
||||
List<Integer> accountNumbers = new ArrayList<Integer>(accounts.length);
|
||||
List<Account> accounts = preferences.getAccounts();
|
||||
List<Integer> accountNumbers = new ArrayList<Integer>(accounts.size());
|
||||
for (Account a : accounts) {
|
||||
accountNumbers.add(a.getAccountNumber());
|
||||
}
|
||||
@ -679,10 +679,10 @@ public class Account implements BaseAccount {
|
||||
*
|
||||
* I bet there is a much smarter way to do this. Anyone like to suggest it?
|
||||
*/
|
||||
Account[] accounts = preferences.getAccounts();
|
||||
int[] accountNumbers = new int[accounts.length];
|
||||
for (int i = 0; i < accounts.length; i++) {
|
||||
accountNumbers[i] = accounts[i].getAccountNumber();
|
||||
List<Account> accounts = preferences.getAccounts();
|
||||
int[] accountNumbers = new int[accounts.size()];
|
||||
for (int i = 0; i < accounts.size(); i++) {
|
||||
accountNumbers[i] = accounts.get(i).getAccountNumber();
|
||||
}
|
||||
Arrays.sort(accountNumbers);
|
||||
for (int accountNumber : accountNumbers) {
|
||||
|
@ -5,6 +5,7 @@ import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
|
||||
@ -35,7 +36,7 @@ import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.local.LocalStore;
|
||||
import com.fsck.k9.provider.UnreadWidgetProvider;
|
||||
import com.fsck.k9.security.LocalKeyStore;
|
||||
import com.fsck.k9.service.BootReceiver;
|
||||
@ -88,7 +89,7 @@ public class K9 extends Application {
|
||||
*
|
||||
* @see ApplicationAware
|
||||
*/
|
||||
private static List<ApplicationAware> observers = new ArrayList<ApplicationAware>();
|
||||
private static final List<ApplicationAware> observers = new ArrayList<ApplicationAware>();
|
||||
|
||||
/**
|
||||
* This will be {@code true} once the initialization is complete and {@link #notifyObservers()}
|
||||
@ -263,7 +264,7 @@ public class K9 extends Application {
|
||||
private static boolean mHideTimeZone = false;
|
||||
|
||||
private static SortType mSortType;
|
||||
private static HashMap<SortType, Boolean> mSortAscending = new HashMap<SortType, Boolean>();
|
||||
private static Map<SortType, Boolean> mSortAscending = new HashMap<SortType, Boolean>();
|
||||
|
||||
private static boolean sUseBackgroundAsUnreadIndicator = true;
|
||||
private static boolean sThreadedViewEnabled = true;
|
||||
|
@ -19,7 +19,7 @@ import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
@ -284,10 +284,7 @@ public final class PRNGFixes {
|
||||
if (serial != null) {
|
||||
result.append(serial);
|
||||
}
|
||||
try {
|
||||
return result.toString().getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("UTF-8 encoding not supported");
|
||||
}
|
||||
|
||||
return result.toString().getBytes(Charset.forName("UTF-8"));
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.fsck.k9;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -18,11 +19,6 @@ import com.fsck.k9.preferences.Storage;
|
||||
|
||||
public class Preferences {
|
||||
|
||||
/**
|
||||
* Immutable empty {@link Account} array
|
||||
*/
|
||||
private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[0];
|
||||
|
||||
private static Preferences preferences;
|
||||
|
||||
public static synchronized Preferences getPreferences(Context context) {
|
||||
@ -43,7 +39,7 @@ public class Preferences {
|
||||
private Preferences(Context context) {
|
||||
mStorage = Storage.getStorage(context);
|
||||
mContext = context;
|
||||
if (mStorage.size() == 0) {
|
||||
if (mStorage.isEmpty()) {
|
||||
Log.i(K9.LOG_TAG, "Preferences storage is zero-size, importing from Android-style preferences");
|
||||
Editor editor = mStorage.edit();
|
||||
editor.copy(context.getSharedPreferences("AndroidMail.Main", Context.MODE_PRIVATE));
|
||||
@ -75,12 +71,12 @@ public class Preferences {
|
||||
* registered the method returns an empty array.
|
||||
* @return all accounts
|
||||
*/
|
||||
public synchronized Account[] getAccounts() {
|
||||
public synchronized List<Account> getAccounts() {
|
||||
if (accounts == null) {
|
||||
loadAccounts();
|
||||
}
|
||||
|
||||
return accountsInOrder.toArray(EMPTY_ACCOUNT_ARRAY);
|
||||
return Collections.unmodifiableList(accountsInOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,7 +85,7 @@ public class Preferences {
|
||||
* @return all accounts with {@link Account#isAvailable(Context)}
|
||||
*/
|
||||
public synchronized Collection<Account> getAvailableAccounts() {
|
||||
Account[] allAccounts = getAccounts();
|
||||
List<Account> allAccounts = getAccounts();
|
||||
Collection<Account> retval = new ArrayList<Account>(accounts.size());
|
||||
for (Account account : allAccounts) {
|
||||
if (account.isEnabled() && account.isAvailable(mContext)) {
|
||||
|
@ -68,7 +68,7 @@ public abstract class AccountList extends K9ListActivity implements OnItemClickL
|
||||
* @param realAccounts
|
||||
* An array of accounts to display.
|
||||
*/
|
||||
public void populateListView(Account[] realAccounts) {
|
||||
public void populateListView(List<Account> realAccounts) {
|
||||
List<BaseAccount> accounts = new ArrayList<BaseAccount>();
|
||||
|
||||
if (displaySpecialAccounts() && !K9.isHideSpecialAccounts()) {
|
||||
@ -79,7 +79,7 @@ public abstract class AccountList extends K9ListActivity implements OnItemClickL
|
||||
accounts.add(allMessagesAccount);
|
||||
}
|
||||
|
||||
accounts.addAll(Arrays.asList(realAccounts));
|
||||
accounts.addAll(realAccounts);
|
||||
AccountsAdapter adapter = new AccountsAdapter(accounts);
|
||||
ListView listView = getListView();
|
||||
listView.setAdapter(adapter);
|
||||
@ -169,15 +169,15 @@ public abstract class AccountList extends K9ListActivity implements OnItemClickL
|
||||
/**
|
||||
* Load accounts in a background thread
|
||||
*/
|
||||
class LoadAccounts extends AsyncTask<Void, Void, Account[]> {
|
||||
class LoadAccounts extends AsyncTask<Void, Void, List<Account>> {
|
||||
@Override
|
||||
protected Account[] doInBackground(Void... params) {
|
||||
Account[] accounts = Preferences.getPreferences(getApplicationContext()).getAccounts();
|
||||
protected List<Account> doInBackground(Void... params) {
|
||||
List<Account> accounts = Preferences.getPreferences(getApplicationContext()).getAccounts();
|
||||
return accounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Account[] accounts) {
|
||||
protected void onPostExecute(List<Account> accounts) {
|
||||
populateListView(accounts);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
@ -75,6 +76,7 @@ import com.fsck.k9.activity.setup.Prefs;
|
||||
import com.fsck.k9.activity.setup.WelcomeMessage;
|
||||
import com.fsck.k9.controller.MessagingController;
|
||||
import com.fsck.k9.helper.SizeFormatter;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.AuthType;
|
||||
import com.fsck.k9.mail.ServerSettings;
|
||||
import com.fsck.k9.mail.Store;
|
||||
@ -100,11 +102,6 @@ import de.cketti.library.changelog.ChangeLog;
|
||||
|
||||
public class Accounts extends K9ListActivity implements OnItemClickListener {
|
||||
|
||||
/**
|
||||
* Immutable empty {@link BaseAccount} array
|
||||
*/
|
||||
private static final BaseAccount[] EMPTY_BASE_ACCOUNT_ARRAY = new BaseAccount[0];
|
||||
|
||||
/**
|
||||
* URL used to open Android Market application
|
||||
*/
|
||||
@ -120,9 +117,12 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
|
||||
private static final int DIALOG_RECREATE_ACCOUNT = 3;
|
||||
private static final int DIALOG_NO_FILE_MANAGER = 4;
|
||||
|
||||
/*
|
||||
* Must be serializable hence implementation class used for declaration.
|
||||
*/
|
||||
private ConcurrentHashMap<String, AccountStats> accountStats = new ConcurrentHashMap<String, AccountStats>();
|
||||
|
||||
private ConcurrentHashMap<BaseAccount, String> pendingWork = new ConcurrentHashMap<BaseAccount, String>();
|
||||
private ConcurrentMap<BaseAccount, String> pendingWork = new ConcurrentHashMap<BaseAccount, String>();
|
||||
|
||||
private BaseAccount mSelectedContextAccount;
|
||||
private int mUnreadMessageCount = 0;
|
||||
@ -398,14 +398,14 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
|
||||
createSpecialAccounts();
|
||||
}
|
||||
|
||||
Account[] accounts = Preferences.getPreferences(this).getAccounts();
|
||||
List<Account> accounts = Preferences.getPreferences(this).getAccounts();
|
||||
Intent intent = getIntent();
|
||||
//onNewIntent(intent);
|
||||
|
||||
// see if we should show the welcome message
|
||||
if (ACTION_IMPORT_SETTINGS.equals(intent.getAction())) {
|
||||
onImport();
|
||||
} else if (accounts.length < 1) {
|
||||
} else if (accounts.size() < 1) {
|
||||
WelcomeMessage.showWelcomeMessage(this);
|
||||
finish();
|
||||
return;
|
||||
@ -421,7 +421,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
|
||||
onOpenAccount(mUnifiedInboxAccount);
|
||||
finish();
|
||||
return;
|
||||
} else if (startup && accounts.length == 1 && onOpenAccount(accounts[0])) {
|
||||
} else if (startup && accounts.size() == 1 && onOpenAccount(accounts.get(0))) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
@ -541,18 +541,18 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
|
||||
return retain;
|
||||
}
|
||||
|
||||
private BaseAccount[] accounts = new BaseAccount[0];
|
||||
private List<BaseAccount> accounts = new ArrayList<BaseAccount>();
|
||||
private enum ACCOUNT_LOCATION {
|
||||
TOP, MIDDLE, BOTTOM;
|
||||
}
|
||||
private EnumSet<ACCOUNT_LOCATION> accountLocation(BaseAccount account) {
|
||||
EnumSet<ACCOUNT_LOCATION> accountLocation = EnumSet.of(ACCOUNT_LOCATION.MIDDLE);
|
||||
if (accounts.length > 0) {
|
||||
if (accounts[0].equals(account)) {
|
||||
if (accounts.size() > 0) {
|
||||
if (accounts.get(0).equals(account)) {
|
||||
accountLocation.remove(ACCOUNT_LOCATION.MIDDLE);
|
||||
accountLocation.add(ACCOUNT_LOCATION.TOP);
|
||||
}
|
||||
if (accounts[accounts.length - 1].equals(account)) {
|
||||
if (accounts.get(accounts.size() - 1).equals(account)) {
|
||||
accountLocation.remove(ACCOUNT_LOCATION.MIDDLE);
|
||||
accountLocation.add(ACCOUNT_LOCATION.BOTTOM);
|
||||
}
|
||||
@ -562,7 +562,8 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
|
||||
|
||||
|
||||
private void refresh() {
|
||||
accounts = Preferences.getPreferences(this).getAccounts();
|
||||
accounts.clear();
|
||||
accounts.addAll(Preferences.getPreferences(this).getAccounts());
|
||||
|
||||
// see if we should show the welcome message
|
||||
// if (accounts.length < 1) {
|
||||
@ -571,22 +572,22 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
|
||||
// }
|
||||
|
||||
List<BaseAccount> newAccounts;
|
||||
if (!K9.isHideSpecialAccounts() && accounts.length > 0) {
|
||||
if (!K9.isHideSpecialAccounts() && accounts.size() > 0) {
|
||||
if (mUnifiedInboxAccount == null || mAllMessagesAccount == null) {
|
||||
createSpecialAccounts();
|
||||
}
|
||||
|
||||
newAccounts = new ArrayList<BaseAccount>(accounts.length +
|
||||
newAccounts = new ArrayList<BaseAccount>(accounts.size() +
|
||||
SPECIAL_ACCOUNTS_COUNT);
|
||||
newAccounts.add(mUnifiedInboxAccount);
|
||||
newAccounts.add(mAllMessagesAccount);
|
||||
} else {
|
||||
newAccounts = new ArrayList<BaseAccount>(accounts.length);
|
||||
newAccounts = new ArrayList<BaseAccount>(accounts.size());
|
||||
}
|
||||
|
||||
newAccounts.addAll(Arrays.asList(accounts));
|
||||
newAccounts.addAll(accounts);
|
||||
|
||||
mAdapter = new AccountsAdapter(newAccounts.toArray(EMPTY_BASE_ACCOUNT_ARRAY));
|
||||
mAdapter = new AccountsAdapter(newAccounts);
|
||||
getListView().setAdapter(mAdapter);
|
||||
if (!newAccounts.isEmpty()) {
|
||||
mHandler.progress(Window.PROGRESS_START);
|
||||
@ -1735,7 +1736,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
|
||||
}
|
||||
|
||||
class AccountsAdapter extends ArrayAdapter<BaseAccount> {
|
||||
public AccountsAdapter(BaseAccount[] accounts) {
|
||||
public AccountsAdapter(List<BaseAccount> accounts) {
|
||||
super(Accounts.this, 0, accounts);
|
||||
}
|
||||
|
||||
|
@ -261,7 +261,7 @@ public class ChooseFolder extends K9ListActivity {
|
||||
mHandler.progress(false);
|
||||
}
|
||||
@Override
|
||||
public void listFolders(Account account, Folder[] folders) {
|
||||
public void listFolders(Account account, List<? extends Folder> folders) {
|
||||
if (!account.equals(mAccount)) {
|
||||
return;
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
|
||||
import com.fsck.k9.mail.Folder;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||
import com.fsck.k9.search.LocalSearch;
|
||||
import com.fsck.k9.search.SearchSpecification.Attribute;
|
||||
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
||||
@ -660,7 +660,7 @@ public class FolderList extends K9ListActivity {
|
||||
}
|
||||
|
||||
class FolderListAdapter extends BaseAdapter implements Filterable {
|
||||
private ArrayList<FolderInfoHolder> mFolders = new ArrayList<FolderInfoHolder>();
|
||||
private List<FolderInfoHolder> mFolders = new ArrayList<FolderInfoHolder>();
|
||||
private List<FolderInfoHolder> mFilteredFolders = Collections.unmodifiableList(mFolders);
|
||||
private Filter mFilter = new FolderListFilter();
|
||||
|
||||
@ -740,7 +740,7 @@ public class FolderList extends K9ListActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listFolders(Account account, Folder[] folders) {
|
||||
public void listFolders(Account account, List<? extends Folder> folders) {
|
||||
if (account.equals(mAccount)) {
|
||||
|
||||
List<FolderInfoHolder> newFolders = new LinkedList<FolderInfoHolder>();
|
||||
@ -1176,7 +1176,7 @@ public class FolderList extends K9ListActivity {
|
||||
|
||||
Locale locale = Locale.getDefault();
|
||||
if ((searchTerm == null) || (searchTerm.length() == 0)) {
|
||||
ArrayList<FolderInfoHolder> list = new ArrayList<FolderInfoHolder>(mFolders);
|
||||
List<FolderInfoHolder> list = new ArrayList<FolderInfoHolder>(mFolders);
|
||||
results.values = list;
|
||||
results.count = list.size();
|
||||
} else {
|
||||
@ -1184,7 +1184,7 @@ public class FolderList extends K9ListActivity {
|
||||
final String[] words = searchTermString.split(" ");
|
||||
final int wordCount = words.length;
|
||||
|
||||
final ArrayList<FolderInfoHolder> newValues = new ArrayList<FolderInfoHolder>();
|
||||
final List<FolderInfoHolder> newValues = new ArrayList<FolderInfoHolder>();
|
||||
|
||||
for (final FolderInfoHolder value : mFolders) {
|
||||
if (value.displayName == null) {
|
||||
|
@ -29,7 +29,7 @@ public class FolderListFilter<T> extends Filter {
|
||||
/**
|
||||
* All folders.
|
||||
*/
|
||||
private ArrayList<T> mOriginalValues = null;
|
||||
private List<T> mOriginalValues = null;
|
||||
|
||||
/**
|
||||
* Create a filter for a list of folders.
|
||||
@ -62,7 +62,7 @@ public class FolderListFilter<T> extends Filter {
|
||||
|
||||
Locale locale = Locale.getDefault();
|
||||
if ((searchTerm == null) || (searchTerm.length() == 0)) {
|
||||
ArrayList<T> list = new ArrayList<T>(mOriginalValues);
|
||||
List<T> list = new ArrayList<T>(mOriginalValues);
|
||||
results.values = list;
|
||||
results.count = list.size();
|
||||
} else {
|
||||
@ -70,9 +70,9 @@ public class FolderListFilter<T> extends Filter {
|
||||
final String[] words = searchTermString.split(" ");
|
||||
final int wordCount = words.length;
|
||||
|
||||
final ArrayList<T> values = mOriginalValues;
|
||||
final List<T> values = mOriginalValues;
|
||||
|
||||
final ArrayList<T> newValues = new ArrayList<T>();
|
||||
final List<T> newValues = new ArrayList<T>();
|
||||
|
||||
for (final T value : values) {
|
||||
final String valueText = value.toString().toLowerCase(locale);
|
||||
|
@ -5,9 +5,11 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -107,9 +109,9 @@ import com.fsck.k9.mail.internet.MimeMultipart;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.internet.TextBody;
|
||||
import com.fsck.k9.mail.internet.TextBodyBuilder;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBody;
|
||||
import com.fsck.k9.mail.store.LocalStore.TempFileBody;
|
||||
import com.fsck.k9.mail.store.LocalStore.TempFileMessageBody;
|
||||
import com.fsck.k9.mail.store.local.LocalAttachmentBody;
|
||||
import com.fsck.k9.mail.store.local.TempFileBody;
|
||||
import com.fsck.k9.mail.store.local.TempFileMessageBody;
|
||||
import com.fsck.k9.view.MessageWebView;
|
||||
|
||||
import org.apache.james.mime4j.codec.EncoderUtil;
|
||||
@ -190,8 +192,6 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
private static final int CONTACT_PICKER_CC2 = 8;
|
||||
private static final int CONTACT_PICKER_BCC2 = 9;
|
||||
|
||||
private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[0];
|
||||
|
||||
private static final int REQUEST_CODE_SIGN_ENCRYPT = 12;
|
||||
|
||||
/**
|
||||
@ -1012,7 +1012,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
addAttachment(stream, type);
|
||||
}
|
||||
} else {
|
||||
ArrayList<Parcelable> list = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
List<Parcelable> list = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
if (list != null) {
|
||||
for (Parcelable parcelable : list) {
|
||||
Uri stream = (Uri) parcelable;
|
||||
@ -1056,7 +1056,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
}
|
||||
|
||||
private boolean addRecipients(TextView view, List<String> recipients) {
|
||||
if (recipients == null || recipients.size() == 0) {
|
||||
if (recipients == null || recipients.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1198,7 +1198,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
"\" from saved instance state", e);
|
||||
}
|
||||
|
||||
ArrayList<Attachment> attachments = savedInstanceState.getParcelableArrayList(STATE_KEY_ATTACHMENTS);
|
||||
List<Attachment> attachments = savedInstanceState.getParcelableArrayList(STATE_KEY_ATTACHMENTS);
|
||||
for (Attachment attachment : attachments) {
|
||||
addAttachmentView(attachment);
|
||||
if (attachment.loaderId > mMaxLoaderId) {
|
||||
@ -1807,7 +1807,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
String[] emailsArray = null;
|
||||
if (mEncryptCheckbox.isChecked()) {
|
||||
// get emails as array
|
||||
ArrayList<String> emails = new ArrayList<String>();
|
||||
List<String> emails = new ArrayList<String>();
|
||||
|
||||
for (Address address : getRecipientAddresses()) {
|
||||
emails.add(address.getAddress());
|
||||
@ -1890,13 +1890,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
private InputStream getOpenPgpInputStream() {
|
||||
String text = buildText(false).getText();
|
||||
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new ByteArrayInputStream(text.getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(K9.LOG_TAG, "UnsupportedEncodingException.", e);
|
||||
}
|
||||
return is;
|
||||
return new ByteArrayInputStream(text.getBytes(Charset.forName("UTF-8")));
|
||||
}
|
||||
|
||||
private void executeOpenPgpMethod(Intent intent) {
|
||||
@ -3921,7 +3915,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
||||
|
||||
List<Object> items = new ArrayList<Object>();
|
||||
Preferences prefs = Preferences.getPreferences(context.getApplicationContext());
|
||||
Account[] accounts = prefs.getAvailableAccounts().toArray(EMPTY_ACCOUNT_ARRAY);
|
||||
Collection<Account> accounts = prefs.getAvailableAccounts();
|
||||
for (Account account : accounts) {
|
||||
items.add(account);
|
||||
List<Identity> identities = account.getIdentities();
|
||||
|
@ -451,10 +451,10 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
|
||||
|
||||
String[] accountUuids = mSearch.getAccountUuids();
|
||||
if (mSearch.searchAllAccounts()) {
|
||||
Account[] accounts = prefs.getAccounts();
|
||||
mSingleAccountMode = (accounts.length == 1);
|
||||
List<Account> accounts = prefs.getAccounts();
|
||||
mSingleAccountMode = (accounts.size() == 1);
|
||||
if (mSingleAccountMode) {
|
||||
mAccount = accounts[0];
|
||||
mAccount = accounts.get(0);
|
||||
}
|
||||
} else {
|
||||
mSingleAccountMode = (accountUuids.length == 1);
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.fsck.k9.activity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
@ -14,6 +16,7 @@ import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.service.NotificationActionService;
|
||||
|
||||
public class NotificationDeleteConfirmation extends Activity {
|
||||
@ -25,7 +28,7 @@ public class NotificationDeleteConfirmation extends Activity {
|
||||
private Account mAccount;
|
||||
private ArrayList<MessageReference> mMessageRefs;
|
||||
|
||||
public static PendingIntent getIntent(Context context, final Account account, final ArrayList<MessageReference> refs) {
|
||||
public static PendingIntent getIntent(Context context, final Account account, final Serializable refs) {
|
||||
Intent i = new Intent(context, NotificationDeleteConfirmation.class);
|
||||
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
|
||||
i.putExtra(EXTRA_MESSAGE_LIST, refs);
|
||||
|
@ -30,7 +30,7 @@ import android.widget.TextView;
|
||||
* <li>{@link #actionUpgradeDatabases(Context, Intent)} will call {@link K9#areDatabasesUpToDate()}
|
||||
* to check if we already know whether the databases have been upgraded.</li>
|
||||
* <li>{@link K9#areDatabasesUpToDate()} will compare the last known database version stored in a
|
||||
* {@link SharedPreferences} file to {@link com.fsck.k9.mail.store.LocalStore#DB_VERSION}. This
|
||||
* {@link SharedPreferences} file to {@link com.fsck.k9.mail.store.local.LocalStore#DB_VERSION}. This
|
||||
* is done as an optimization because it's faster than opening all of the accounts' databases
|
||||
* one by one.</li>
|
||||
* <li>If there was an error reading the cached database version or if it shows the databases need
|
||||
|
@ -41,7 +41,7 @@ import com.fsck.k9.activity.ManageIdentities;
|
||||
import com.fsck.k9.crypto.Apg;
|
||||
import com.fsck.k9.mail.Folder;
|
||||
import com.fsck.k9.mail.Store;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||
import com.fsck.k9.mail.store.StorageManager;
|
||||
import com.fsck.k9.service.MailService;
|
||||
|
||||
|
@ -3,10 +3,8 @@ package com.fsck.k9.activity.setup;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Locale;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
@ -35,6 +33,7 @@ import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.activity.K9Activity;
|
||||
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
|
||||
import com.fsck.k9.helper.UrlEncodingHelper;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.AuthType;
|
||||
import com.fsck.k9.mail.ConnectionSecurity;
|
||||
@ -281,8 +280,8 @@ public class AccountSetupBasics extends K9Activity
|
||||
URI incomingUri = null;
|
||||
URI outgoingUri = null;
|
||||
try {
|
||||
String userEnc = URLEncoder.encode(user, "UTF-8");
|
||||
String passwordEnc = URLEncoder.encode(password, "UTF-8");
|
||||
String userEnc = UrlEncodingHelper.encodeUtf8(user);
|
||||
String passwordEnc = UrlEncodingHelper.encodeUtf8(password);
|
||||
|
||||
String incomingUsername = mProvider.incomingUsernameTemplate;
|
||||
incomingUsername = incomingUsername.replaceAll("\\$email", email);
|
||||
@ -338,9 +337,6 @@ public class AccountSetupBasics extends K9Activity
|
||||
}
|
||||
// Check incoming here. Then check outgoing in onActivityResult()
|
||||
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.INCOMING);
|
||||
} catch (UnsupportedEncodingException enc) {
|
||||
// This really shouldn't happen since the encoding is hardcoded to UTF-8
|
||||
Log.e(K9.LOG_TAG, "Couldn't urlencode username or password.", enc);
|
||||
} catch (URISyntaxException use) {
|
||||
/*
|
||||
* If there is some problem with the URI we give up and go on to
|
||||
|
@ -16,8 +16,8 @@ import com.fsck.k9.mail.Folder.FolderClass;
|
||||
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Store;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||
import com.fsck.k9.mail.store.local.LocalStore;
|
||||
import com.fsck.k9.service.MailService;
|
||||
|
||||
public class FolderSettings extends K9PreferenceActivity {
|
||||
|
16
src/com/fsck/k9/cache/EmailProviderCache.java
vendored
16
src/com/fsck/k9/cache/EmailProviderCache.java
vendored
@ -11,8 +11,8 @@ import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
import com.fsck.k9.fragment.MessageListFragment;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||
import com.fsck.k9.mail.store.local.LocalMessage;
|
||||
import com.fsck.k9.provider.EmailProvider;
|
||||
|
||||
/**
|
||||
@ -42,9 +42,9 @@ public class EmailProviderCache {
|
||||
|
||||
|
||||
private String mAccountUuid;
|
||||
private Map<Long, Map<String, String>> mMessageCache = new HashMap<Long, Map<String, String>>();
|
||||
private Map<Long, Map<String, String>> mThreadCache = new HashMap<Long, Map<String, String>>();
|
||||
private Map<Long, Long> mHiddenMessageCache = new HashMap<Long, Long>();
|
||||
private final Map<Long, Map<String, String>> mMessageCache = new HashMap<Long, Map<String, String>>();
|
||||
private final Map<Long, Map<String, String>> mThreadCache = new HashMap<Long, Map<String, String>>();
|
||||
private final Map<Long, Long> mHiddenMessageCache = new HashMap<Long, Long>();
|
||||
|
||||
|
||||
private EmailProviderCache(String accountUuid) {
|
||||
@ -101,7 +101,7 @@ public class EmailProviderCache {
|
||||
Map<String, String> map = mMessageCache.get(messageId);
|
||||
if (map != null) {
|
||||
map.remove(columnName);
|
||||
if (map.size() == 0) {
|
||||
if (map.isEmpty()) {
|
||||
mMessageCache.remove(messageId);
|
||||
}
|
||||
}
|
||||
@ -115,7 +115,7 @@ public class EmailProviderCache {
|
||||
Map<String, String> map = mThreadCache.get(threadRootId);
|
||||
if (map != null) {
|
||||
map.remove(columnName);
|
||||
if (map.size() == 0) {
|
||||
if (map.isEmpty()) {
|
||||
mThreadCache.remove(threadRootId);
|
||||
}
|
||||
}
|
||||
@ -143,7 +143,7 @@ public class EmailProviderCache {
|
||||
}
|
||||
}
|
||||
|
||||
public void unhideMessages(Message[] messages) {
|
||||
public void unhideMessages(List<? extends Message> messages) {
|
||||
synchronized (mHiddenMessageCache) {
|
||||
for (Message message : messages) {
|
||||
LocalMessage localMessage = (LocalMessage) message;
|
||||
|
@ -108,7 +108,7 @@ public class EmailProviderCacheCursor extends CursorWrapper {
|
||||
|
||||
@Override
|
||||
public boolean moveToPosition(int position) {
|
||||
if (mHiddenRows.size() == 0) {
|
||||
if (mHiddenRows.isEmpty()) {
|
||||
return super.moveToPosition(position);
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ public class EmailProviderCacheCursor extends CursorWrapper {
|
||||
|
||||
@Override
|
||||
public int getPosition() {
|
||||
if (mHiddenRows.size() == 0) {
|
||||
if (mHiddenRows.isEmpty()) {
|
||||
return super.getPosition();
|
||||
}
|
||||
|
||||
|
@ -2,12 +2,14 @@ package com.fsck.k9.controller;
|
||||
|
||||
import java.io.CharArrayWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@ -79,10 +81,10 @@ import com.fsck.k9.mail.Transport;
|
||||
import com.fsck.k9.mail.internet.MimeMessage;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.internet.TextBody;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
||||
import com.fsck.k9.mail.store.LocalStore.PendingCommand;
|
||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||
import com.fsck.k9.mail.store.local.LocalMessage;
|
||||
import com.fsck.k9.mail.store.local.LocalStore;
|
||||
import com.fsck.k9.mail.store.local.LocalStore.PendingCommand;
|
||||
import com.fsck.k9.mail.store.Pop3Store;
|
||||
import com.fsck.k9.mail.store.UnavailableAccountException;
|
||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
||||
@ -116,16 +118,6 @@ public class MessagingController implements Runnable {
|
||||
*/
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
/**
|
||||
* Immutable empty {@link Message} array
|
||||
*/
|
||||
private static final Message[] EMPTY_MESSAGE_ARRAY = new Message[0];
|
||||
|
||||
/**
|
||||
* Immutable empty {@link Folder} array
|
||||
*/
|
||||
private static final Folder[] EMPTY_FOLDER_ARRAY = new Folder[0];
|
||||
|
||||
/**
|
||||
* The maximum message size that we'll consider to be "small". A small message is downloaded
|
||||
* in full immediately instead of in pieces. Anything over this size will be downloaded in
|
||||
@ -295,17 +287,14 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of references for all pending messages for the notification.
|
||||
*
|
||||
* @return Message reference list
|
||||
* Adds a list of references for all pending messages for the notification to the supplied
|
||||
* List.
|
||||
*/
|
||||
public ArrayList<MessageReference> getAllMessageRefs() {
|
||||
ArrayList<MessageReference> refs = new ArrayList<MessageReference>();
|
||||
public void supplyAllMessageRefs(List<MessageReference> refs) {
|
||||
for (Message m : messages) {
|
||||
refs.add(m.makeMessageReference());
|
||||
}
|
||||
refs.addAll(droppedMessages);
|
||||
return refs;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -319,10 +308,9 @@ public class MessagingController implements Runnable {
|
||||
};
|
||||
|
||||
// Key is accountNumber
|
||||
private ConcurrentHashMap<Integer, NotificationData> notificationData = new ConcurrentHashMap<Integer, NotificationData>();
|
||||
|
||||
private static final Flag[] SYNC_FLAGS = new Flag[] { Flag.SEEN, Flag.FLAGGED, Flag.ANSWERED, Flag.FORWARDED };
|
||||
private final ConcurrentMap<Integer, NotificationData> notificationData = new ConcurrentHashMap<Integer, NotificationData>();
|
||||
|
||||
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) {
|
||||
EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(),
|
||||
@ -330,7 +318,7 @@ public class MessagingController implements Runnable {
|
||||
cache.hideMessages(messages);
|
||||
}
|
||||
|
||||
private void unsuppressMessages(Account account, Message[] messages) {
|
||||
private void unsuppressMessages(Account account, List<? extends Message> messages) {
|
||||
EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(),
|
||||
mApplication.getApplicationContext());
|
||||
cache.unhideMessages(messages);
|
||||
@ -574,15 +562,13 @@ public class MessagingController implements Runnable {
|
||||
Store localStore = account.getLocalStore();
|
||||
localFolders = localStore.getPersonalNamespaces(false);
|
||||
|
||||
Folder[] folderArray = localFolders.toArray(EMPTY_FOLDER_ARRAY);
|
||||
|
||||
if (refreshRemote || localFolders.isEmpty()) {
|
||||
doRefreshRemote(account, listener);
|
||||
return;
|
||||
}
|
||||
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
l.listFolders(account, folderArray);
|
||||
l.listFolders(account, localFolders);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
@ -654,10 +640,9 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
|
||||
localFolders = localStore.getPersonalNamespaces(false);
|
||||
Folder[] folderArray = localFolders.toArray(EMPTY_FOLDER_ARRAY);
|
||||
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
l.listFolders(account, folderArray);
|
||||
l.listFolders(account, localFolders);
|
||||
}
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
l.listFoldersFinished(account);
|
||||
@ -694,7 +679,7 @@ public class MessagingController implements Runnable {
|
||||
public void searchLocalMessagesSynchronous(final LocalSearch search, final MessagingListener listener) {
|
||||
final AccountStats stats = new AccountStats();
|
||||
final Set<String> uuidSet = new HashSet<String>(Arrays.asList(search.getAccountUuids()));
|
||||
Account[] accounts = Preferences.getPreferences(mApplication.getApplicationContext()).getAccounts();
|
||||
List<Account> accounts = Preferences.getPreferences(mApplication.getApplicationContext()).getAccounts();
|
||||
boolean allAccounts = uuidSet.contains(SearchSpecification.ALL_ACCOUNTS);
|
||||
|
||||
// for every account we want to search do the query in the localstore
|
||||
@ -755,7 +740,7 @@ public class MessagingController implements Runnable {
|
||||
|
||||
|
||||
public Future<?> searchRemoteMessages(final String acctUuid, final String folderName, final String query,
|
||||
final Flag[] requiredFlags, final Flag[] forbiddenFlags, final MessagingListener listener) {
|
||||
final Set<Flag> requiredFlags, final Set<Flag> forbiddenFlags, final MessagingListener listener) {
|
||||
if (K9.DEBUG) {
|
||||
String msg = "searchRemoteMessages ("
|
||||
+ "acct=" + acctUuid
|
||||
@ -773,7 +758,7 @@ public class MessagingController implements Runnable {
|
||||
});
|
||||
}
|
||||
public void searchRemoteMessagesSynchronous(final String acctUuid, final String folderName, final String query,
|
||||
final Flag[] requiredFlags, final Flag[] forbiddenFlags, final MessagingListener listener) {
|
||||
final Set<Flag> requiredFlags, final Set<Flag> forbiddenFlags, final MessagingListener listener) {
|
||||
final Account acct = Preferences.getPreferences(mApplication.getApplicationContext()).getAccount(acctUuid);
|
||||
|
||||
if (listener != null) {
|
||||
@ -885,10 +870,10 @@ public class MessagingController implements Runnable {
|
||||
LocalMessage localMsg = localFolder.getMessage(message.getUid());
|
||||
|
||||
if (localMsg == null) {
|
||||
remoteFolder.fetch(new Message [] {message}, header, null);
|
||||
remoteFolder.fetch(Collections.singletonList(message), header, null);
|
||||
//fun fact: ImapFolder.fetch can't handle getting STRUCTURE at same time as headers
|
||||
remoteFolder.fetch(new Message [] {message}, structure, null);
|
||||
localFolder.appendMessages(new Message [] {message});
|
||||
remoteFolder.fetch(Collections.singletonList(message), structure, null);
|
||||
localFolder.appendMessages(Collections.singletonList(message));
|
||||
localMsg = localFolder.getMessage(message.getUid());
|
||||
}
|
||||
|
||||
@ -992,8 +977,8 @@ public class MessagingController implements Runnable {
|
||||
final LocalFolder localFolder = tLocalFolder;
|
||||
localFolder.open(Folder.OPEN_MODE_RW);
|
||||
localFolder.updateLastUid();
|
||||
Message[] localMessages = localFolder.getMessages(null);
|
||||
HashMap<String, Message> localUidMap = new HashMap<String, Message>();
|
||||
List<? extends Message> localMessages = localFolder.getMessages(null);
|
||||
Map<String, Message> localUidMap = new HashMap<String, Message>();
|
||||
for (Message message : localMessages) {
|
||||
localUidMap.put(message.getUid(), message);
|
||||
}
|
||||
@ -1058,9 +1043,8 @@ public class MessagingController implements Runnable {
|
||||
visibleLimit = K9.DEFAULT_VISIBLE_LIMIT;
|
||||
}
|
||||
|
||||
Message[] remoteMessageArray = EMPTY_MESSAGE_ARRAY;
|
||||
final ArrayList<Message> remoteMessages = new ArrayList<Message>();
|
||||
HashMap<String, Message> remoteUidMap = new HashMap<String, Message>();
|
||||
final List<Message> remoteMessages = new ArrayList<Message>();
|
||||
Map<String, Message> remoteUidMap = new HashMap<String, Message>();
|
||||
|
||||
if (K9.DEBUG)
|
||||
Log.v(K9.LOG_TAG, "SYNC: Remote message count for folder " + folder + " is " + remoteMessageCount);
|
||||
@ -1086,9 +1070,9 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
|
||||
|
||||
remoteMessageArray = remoteFolder.getMessages(remoteStart, remoteEnd, earliestDate, null);
|
||||
List<? extends Message> remoteMessageArray = remoteFolder.getMessages(remoteStart, remoteEnd, earliestDate, null);
|
||||
|
||||
int messageCount = remoteMessageArray.length;
|
||||
int messageCount = remoteMessageArray.size();
|
||||
|
||||
for (Message thisMess : remoteMessageArray) {
|
||||
headerProgress.incrementAndGet();
|
||||
@ -1104,7 +1088,6 @@ public class MessagingController implements Runnable {
|
||||
if (K9.DEBUG)
|
||||
Log.v(K9.LOG_TAG, "SYNC: Got " + remoteUidMap.size() + " messages for folder " + folder);
|
||||
|
||||
remoteMessageArray = null;
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
l.synchronizeMailboxHeadersFinished(account, folder, headerProgress.get(), remoteUidMap.size());
|
||||
}
|
||||
@ -1117,7 +1100,7 @@ public class MessagingController implements Runnable {
|
||||
* Remove any messages that are in the local store but no longer on the remote store or are too old
|
||||
*/
|
||||
if (account.syncRemoteDeletions()) {
|
||||
ArrayList<Message> destroyMessages = new ArrayList<Message>();
|
||||
List<Message> destroyMessages = new ArrayList<Message>();
|
||||
for (Message localMessage : localMessages) {
|
||||
if (remoteUidMap.get(localMessage.getUid()) == null) {
|
||||
destroyMessages.add(localMessage);
|
||||
@ -1125,7 +1108,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
|
||||
|
||||
localFolder.destroyMessages(destroyMessages.toArray(EMPTY_MESSAGE_ARRAY));
|
||||
localFolder.destroyMessages(destroyMessages);
|
||||
|
||||
for (Message destroyMessage : destroyMessages) {
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
@ -1280,7 +1263,7 @@ public class MessagingController implements Runnable {
|
||||
Log.e(K9.LOG_TAG, "Unable to getUnreadMessageCount for account: " + account, e);
|
||||
}
|
||||
|
||||
ArrayList<Message> syncFlagMessages = new ArrayList<Message>();
|
||||
List<Message> syncFlagMessages = new ArrayList<Message>();
|
||||
List<Message> unsyncedMessages = new ArrayList<Message>();
|
||||
final AtomicInteger newMessages = new AtomicInteger(0);
|
||||
|
||||
@ -1300,8 +1283,8 @@ public class MessagingController implements Runnable {
|
||||
Log.d(K9.LOG_TAG, "SYNC: Have " + unsyncedMessages.size() + " unsynced messages");
|
||||
|
||||
messages.clear();
|
||||
final ArrayList<Message> largeMessages = new ArrayList<Message>();
|
||||
final ArrayList<Message> smallMessages = new ArrayList<Message>();
|
||||
final List<Message> largeMessages = new ArrayList<Message>();
|
||||
final List<Message> smallMessages = new ArrayList<Message>();
|
||||
if (!unsyncedMessages.isEmpty()) {
|
||||
|
||||
/*
|
||||
@ -1416,7 +1399,7 @@ public class MessagingController implements Runnable {
|
||||
final Folder remoteFolder,
|
||||
final Account account,
|
||||
final List<Message> unsyncedMessages,
|
||||
final ArrayList<Message> syncFlagMessages,
|
||||
final List<Message> syncFlagMessages,
|
||||
boolean flagSyncOnly) throws MessagingException {
|
||||
if (message.isSet(Flag.DELETED)) {
|
||||
syncFlagMessages.add(message);
|
||||
@ -1437,7 +1420,7 @@ public class MessagingController implements Runnable {
|
||||
Log.v(K9.LOG_TAG, "Message with uid " + message.getUid() + " is partially or fully downloaded");
|
||||
|
||||
// Store the updated message locally
|
||||
localFolder.appendMessages(new Message[] { message });
|
||||
localFolder.appendMessages(Collections.singletonList(message));
|
||||
|
||||
localMessage = localFolder.getMessage(message.getUid());
|
||||
|
||||
@ -1475,8 +1458,8 @@ public class MessagingController implements Runnable {
|
||||
private void fetchUnsyncedMessages(final Account account, final Folder remoteFolder,
|
||||
final LocalFolder localFolder,
|
||||
List<Message> unsyncedMessages,
|
||||
final ArrayList<Message> smallMessages,
|
||||
final ArrayList<Message> largeMessages,
|
||||
final List<Message> smallMessages,
|
||||
final List<Message> largeMessages,
|
||||
final AtomicInteger progress,
|
||||
final int todo,
|
||||
FetchProfile fp) throws MessagingException {
|
||||
@ -1489,7 +1472,7 @@ public class MessagingController implements Runnable {
|
||||
*/
|
||||
final List<Message> chunk = new ArrayList<Message>(UNSYNC_CHUNK_SIZE);
|
||||
|
||||
remoteFolder.fetch(unsyncedMessages.toArray(EMPTY_MESSAGE_ARRAY), fp,
|
||||
remoteFolder.fetch(unsyncedMessages, fp,
|
||||
new MessageRetrievalListener() {
|
||||
@Override
|
||||
public void messageFinished(Message message, int number, int ofTotal) {
|
||||
@ -1578,7 +1561,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
try {
|
||||
// Store the new message locally
|
||||
localFolder.appendMessages(messages.toArray(new Message[messages.size()]));
|
||||
localFolder.appendMessages(messages);
|
||||
|
||||
for (final Message message : messages) {
|
||||
final Message localMessage = localFolder.getMessage(message.getUid());
|
||||
@ -1611,7 +1594,7 @@ public class MessagingController implements Runnable {
|
||||
|
||||
private void downloadSmallMessages(final Account account, final Folder remoteFolder,
|
||||
final LocalFolder localFolder,
|
||||
ArrayList<Message> smallMessages,
|
||||
List<Message> smallMessages,
|
||||
final AtomicInteger progress,
|
||||
final int unreadBeforeStart,
|
||||
final AtomicInteger newMessages,
|
||||
@ -1624,7 +1607,7 @@ public class MessagingController implements Runnable {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "SYNC: Fetching small messages for folder " + folder);
|
||||
|
||||
remoteFolder.fetch(smallMessages.toArray(new Message[smallMessages.size()]),
|
||||
remoteFolder.fetch(smallMessages,
|
||||
fp, new MessageRetrievalListener() {
|
||||
@Override
|
||||
public void messageFinished(final Message message, int number, int ofTotal) {
|
||||
@ -1690,7 +1673,7 @@ public class MessagingController implements Runnable {
|
||||
|
||||
private void downloadLargeMessages(final Account account, final Folder remoteFolder,
|
||||
final LocalFolder localFolder,
|
||||
ArrayList<Message> largeMessages,
|
||||
List<Message> largeMessages,
|
||||
final AtomicInteger progress,
|
||||
final int unreadBeforeStart,
|
||||
final AtomicInteger newMessages,
|
||||
@ -1703,7 +1686,7 @@ public class MessagingController implements Runnable {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "SYNC: Fetching large messages for folder " + folder);
|
||||
|
||||
remoteFolder.fetch(largeMessages.toArray(new Message[largeMessages.size()]), fp, null);
|
||||
remoteFolder.fetch(largeMessages, fp, null);
|
||||
for (Message message : largeMessages) {
|
||||
|
||||
if (!shouldImportMessage(account, folder, message, progress, earliestDate)) {
|
||||
@ -1726,10 +1709,10 @@ public class MessagingController implements Runnable {
|
||||
* they equal we can mark this SYNCHRONIZED instead of PARTIALLY_SYNCHRONIZED
|
||||
*/
|
||||
|
||||
remoteFolder.fetch(new Message[] { message }, fp, null);
|
||||
remoteFolder.fetch(Collections.singletonList(message), fp, null);
|
||||
|
||||
// Store the updated message locally
|
||||
localFolder.appendMessages(new Message[] { message });
|
||||
localFolder.appendMessages(Collections.singletonList(message));
|
||||
|
||||
Message localMessage = localFolder.getMessage(message.getUid());
|
||||
|
||||
@ -1770,7 +1753,7 @@ public class MessagingController implements Runnable {
|
||||
remoteFolder.fetchPart(message, part, null);
|
||||
}
|
||||
// Store the updated message locally
|
||||
localFolder.appendMessages(new Message[] { message });
|
||||
localFolder.appendMessages(Collections.singletonList(message));
|
||||
|
||||
Message localMessage = localFolder.getMessage(message.getUid());
|
||||
|
||||
@ -1815,7 +1798,7 @@ public class MessagingController implements Runnable {
|
||||
|
||||
private void refreshLocalMessageFlags(final Account account, final Folder remoteFolder,
|
||||
final LocalFolder localFolder,
|
||||
ArrayList<Message> syncFlagMessages,
|
||||
List<Message> syncFlagMessages,
|
||||
final AtomicInteger progress,
|
||||
final int todo
|
||||
) throws MessagingException {
|
||||
@ -1836,7 +1819,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
remoteFolder.fetch(undeletedMessages.toArray(EMPTY_MESSAGE_ARRAY), fp, null);
|
||||
remoteFolder.fetch(undeletedMessages, fp, null);
|
||||
for (Message remoteMessage : syncFlagMessages) {
|
||||
Message localMessage = localFolder.getMessage(remoteMessage.getUid());
|
||||
boolean messageChanged = syncFlags(localMessage, remoteMessage);
|
||||
@ -1952,7 +1935,7 @@ public class MessagingController implements Runnable {
|
||||
|
||||
private void processPendingCommandsSynchronous(Account account) throws MessagingException {
|
||||
LocalStore localStore = account.getLocalStore();
|
||||
ArrayList<PendingCommand> commands = localStore.getPendingCommands();
|
||||
List<PendingCommand> commands = localStore.getPendingCommands();
|
||||
|
||||
int progress = 0;
|
||||
int todo = commands.size();
|
||||
@ -2111,10 +2094,10 @@ public class MessagingController implements Runnable {
|
||||
*/
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
localFolder.fetch(new Message[] { localMessage } , fp, null);
|
||||
localFolder.fetch(Collections.singletonList(localMessage) , fp, null);
|
||||
String oldUid = localMessage.getUid();
|
||||
localMessage.setFlag(Flag.X_REMOTE_COPY_STARTED, true);
|
||||
remoteFolder.appendMessages(new Message[] { localMessage });
|
||||
remoteFolder.appendMessages(Collections.singletonList(localMessage));
|
||||
|
||||
localFolder.changeUid(localMessage);
|
||||
for (MessagingListener l : getListeners()) {
|
||||
@ -2129,7 +2112,7 @@ public class MessagingController implements Runnable {
|
||||
*/
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
remoteFolder.fetch(new Message[] { remoteMessage }, fp, null);
|
||||
remoteFolder.fetch(Collections.singletonList(remoteMessage), fp, null);
|
||||
Date localDate = localMessage.getInternalDate();
|
||||
Date remoteDate = remoteMessage.getInternalDate();
|
||||
if (remoteDate != null && remoteDate.compareTo(localDate) > 0) {
|
||||
@ -2146,12 +2129,12 @@ public class MessagingController implements Runnable {
|
||||
fp.clear();
|
||||
fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
localFolder.fetch(new Message[] { localMessage }, fp, null);
|
||||
localFolder.fetch(Collections.singletonList(localMessage), fp, null);
|
||||
String oldUid = localMessage.getUid();
|
||||
|
||||
localMessage.setFlag(Flag.X_REMOTE_COPY_STARTED, true);
|
||||
|
||||
remoteFolder.appendMessages(new Message[] { localMessage });
|
||||
remoteFolder.appendMessages(Collections.singletonList(localMessage));
|
||||
localFolder.changeUid(localMessage);
|
||||
for (MessagingListener l : getListeners()) {
|
||||
l.messageUidChanged(account, folder, oldUid, localMessage.getUid());
|
||||
@ -2325,14 +2308,14 @@ public class MessagingController implements Runnable {
|
||||
if (K9.FOLDER_NONE.equals(destFolderName)) {
|
||||
destFolderName = null;
|
||||
}
|
||||
remoteSrcFolder.delete(messages.toArray(EMPTY_MESSAGE_ARRAY), destFolderName);
|
||||
remoteSrcFolder.delete(messages, destFolderName);
|
||||
} else {
|
||||
remoteDestFolder = remoteStore.getFolder(destFolder);
|
||||
|
||||
if (isCopy) {
|
||||
remoteUidMap = remoteSrcFolder.copyMessages(messages.toArray(EMPTY_MESSAGE_ARRAY), remoteDestFolder);
|
||||
remoteUidMap = remoteSrcFolder.copyMessages(messages, remoteDestFolder);
|
||||
} else {
|
||||
remoteUidMap = remoteSrcFolder.moveMessages(messages.toArray(EMPTY_MESSAGE_ARRAY), remoteDestFolder);
|
||||
remoteUidMap = remoteSrcFolder.moveMessages(messages, remoteDestFolder);
|
||||
}
|
||||
}
|
||||
if (!isCopy && Account.EXPUNGE_IMMEDIATELY.equals(account.getExpungePolicy())) {
|
||||
@ -2425,7 +2408,7 @@ public class MessagingController implements Runnable {
|
||||
if (messages.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
remoteFolder.setFlags(messages.toArray(EMPTY_MESSAGE_ARRAY), new Flag[] { flag }, newState);
|
||||
remoteFolder.setFlags(messages, Collections.singleton(flag), newState);
|
||||
} finally {
|
||||
closeFolder(remoteFolder);
|
||||
}
|
||||
@ -2571,9 +2554,9 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
|
||||
if (isCopy) {
|
||||
remoteSrcFolder.copyMessages(new Message[] { remoteMessage }, remoteDestFolder);
|
||||
remoteSrcFolder.copyMessages(Collections.singletonList(remoteMessage), remoteDestFolder);
|
||||
} else {
|
||||
remoteSrcFolder.moveMessages(new Message[] { remoteMessage }, remoteDestFolder);
|
||||
remoteSrcFolder.moveMessages(Collections.singletonList(remoteMessage), remoteDestFolder);
|
||||
}
|
||||
remoteSrcFolder.close();
|
||||
remoteDestFolder.close();
|
||||
@ -2587,7 +2570,7 @@ public class MessagingController implements Runnable {
|
||||
Store localStore = account.getLocalStore();
|
||||
localFolder = (LocalFolder) localStore.getFolder(folder);
|
||||
localFolder.open(Folder.OPEN_MODE_RW);
|
||||
Message[] messages = localFolder.getMessages(null, false);
|
||||
List<? extends Message> messages = localFolder.getMessages(null, false);
|
||||
for (Message message : messages) {
|
||||
if (!message.isSet(Flag.SEEN)) {
|
||||
message.setFlag(Flag.SEEN, true);
|
||||
@ -2617,7 +2600,7 @@ public class MessagingController implements Runnable {
|
||||
return;
|
||||
}
|
||||
|
||||
remoteFolder.setFlags(new Flag[] {Flag.SEEN}, true);
|
||||
remoteFolder.setFlags(Collections.singleton(Flag.SEEN), true);
|
||||
remoteFolder.close();
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
Log.w(K9.LOG_TAG, "Could not mark all server-side as read because store doesn't support operation", uoe);
|
||||
@ -2727,10 +2710,8 @@ public class MessagingController implements Runnable {
|
||||
|
||||
Store localStore = account.getLocalStore();
|
||||
LocalFolder localFolder = (LocalFolder)localStore.getFolder(account.getErrorFolderName());
|
||||
Message[] messages = new Message[1];
|
||||
MimeMessage message = new MimeMessage();
|
||||
|
||||
|
||||
message.setBody(new TextBody(body));
|
||||
message.setFlag(Flag.X_DOWNLOADED_FULL, true);
|
||||
message.setSubject(subject);
|
||||
@ -2740,9 +2721,8 @@ public class MessagingController implements Runnable {
|
||||
message.setInternalDate(nowDate);
|
||||
message.addSentDate(nowDate);
|
||||
message.setFrom(new Address(account.getEmail(), "K9mail internal"));
|
||||
messages[0] = message;
|
||||
|
||||
localFolder.appendMessages(messages);
|
||||
localFolder.appendMessages(Collections.singletonList(message));
|
||||
|
||||
localFolder.clearMessagesOlderThan(nowTime - (15 * 60 * 1000));
|
||||
|
||||
@ -2874,7 +2854,7 @@ public class MessagingController implements Runnable {
|
||||
* @param newState
|
||||
* {@code true}, if the flag should be set. {@code false} if it should be removed.
|
||||
*/
|
||||
public void setFlag(Account account, String folderName, Message[] messages, Flag flag,
|
||||
public void setFlag(Account account, String folderName, List<Message> messages, Flag flag,
|
||||
boolean newState) {
|
||||
// TODO: Put this into the background, but right now some callers depend on the message
|
||||
// objects being modified right after this method returns.
|
||||
@ -2896,7 +2876,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
|
||||
// Update the messages in the local store
|
||||
localFolder.setFlags(messages, new Flag[] {flag}, newState);
|
||||
localFolder.setFlags(messages, Collections.singleton(flag), newState);
|
||||
|
||||
int unreadMessageCount = localFolder.getUnreadMessageCount();
|
||||
for (MessagingListener l : getListeners()) {
|
||||
@ -2914,9 +2894,9 @@ public class MessagingController implements Runnable {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] uids = new String[messages.length];
|
||||
String[] uids = new String[messages.size()];
|
||||
for (int i = 0, end = uids.length; i < end; i++) {
|
||||
uids[i] = messages[i].getUid();
|
||||
uids[i] = messages.get(i).getUid();
|
||||
}
|
||||
|
||||
queueSetFlag(account, folderName, Boolean.toString(newState), flag.toString(), uids);
|
||||
@ -2953,7 +2933,7 @@ public class MessagingController implements Runnable {
|
||||
|
||||
Message message = localFolder.getMessage(uid);
|
||||
if (message != null) {
|
||||
setFlag(account, folderName, new Message[] { message }, flag, newState);
|
||||
setFlag(account, folderName, Collections.singletonList(message), flag, newState);
|
||||
}
|
||||
} catch (MessagingException me) {
|
||||
addErrorMessage(account, null, me);
|
||||
@ -3026,7 +3006,7 @@ public class MessagingController implements Runnable {
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
localFolder.fetch(new Message[] { message }, fp, null);
|
||||
localFolder.fetch(Collections.singletonList(message), fp, null);
|
||||
} else {
|
||||
/*
|
||||
* At this point the message is not available, so we need to download it
|
||||
@ -3042,16 +3022,16 @@ public class MessagingController implements Runnable {
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
|
||||
remoteFolder.fetch(new Message[] { remoteMessage }, fp, null);
|
||||
remoteFolder.fetch(Collections.singletonList(remoteMessage), fp, null);
|
||||
|
||||
// Store the message locally and load the stored message into memory
|
||||
localFolder.appendMessages(new Message[] { remoteMessage });
|
||||
localFolder.appendMessages(Collections.singletonList(remoteMessage));
|
||||
if (loadPartialFromSearch) {
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
}
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
message = localFolder.getMessage(uid);
|
||||
localFolder.fetch(new Message[] { message }, fp, null);
|
||||
localFolder.fetch(Collections.singletonList(message), fp, null);
|
||||
|
||||
// Mark that this message is now fully synched
|
||||
if (account.isMarkMessageAsReadOnView()) {
|
||||
@ -3124,9 +3104,7 @@ public class MessagingController implements Runnable {
|
||||
FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
localFolder.fetch(new Message[] {
|
||||
message
|
||||
}, fp, null);
|
||||
localFolder.fetch(Collections.singletonList(message), fp, null);
|
||||
localFolder.close();
|
||||
|
||||
for (MessagingListener l : getListeners(listener)) {
|
||||
@ -3266,7 +3244,7 @@ public class MessagingController implements Runnable {
|
||||
LocalStore localStore = account.getLocalStore();
|
||||
LocalFolder localFolder = localStore.getFolder(account.getOutboxFolderName());
|
||||
localFolder.open(Folder.OPEN_MODE_RW);
|
||||
localFolder.appendMessages(new Message[] { message });
|
||||
localFolder.appendMessages(Collections.singletonList(message));
|
||||
Message localMessage = localFolder.getMessage(message.getUid());
|
||||
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
|
||||
localFolder.close();
|
||||
@ -3504,9 +3482,9 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
localFolder.open(Folder.OPEN_MODE_RW);
|
||||
|
||||
Message[] localMessages = localFolder.getMessages(null);
|
||||
List<? extends Message> localMessages = localFolder.getMessages(null);
|
||||
int progress = 0;
|
||||
int todo = localMessages.length;
|
||||
int todo = localMessages.size();
|
||||
for (MessagingListener l : getListeners()) {
|
||||
l.synchronizeMailboxProgress(account, account.getSentFolderName(), progress, todo);
|
||||
}
|
||||
@ -3545,7 +3523,7 @@ public class MessagingController implements Runnable {
|
||||
|
||||
|
||||
|
||||
localFolder.fetch(new Message[] { message }, fp, null);
|
||||
localFolder.fetch(Collections.singletonList(message), fp, null);
|
||||
try {
|
||||
|
||||
|
||||
@ -3576,7 +3554,7 @@ public class MessagingController implements Runnable {
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "Moving sent message to folder '" + account.getSentFolderName() + "' (" + localSentFolder.getId() + ") ");
|
||||
|
||||
localFolder.moveMessages(new Message[] { message }, localSentFolder);
|
||||
localFolder.moveMessages(Collections.singletonList(message), localSentFolder);
|
||||
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "Moved sent message to folder '" + account.getSentFolderName() + "' (" + localSentFolder.getId() + ") ");
|
||||
@ -3594,7 +3572,7 @@ public class MessagingController implements Runnable {
|
||||
// This is a complete hack, but is worlds better than the previous
|
||||
// "don't even bother" functionality
|
||||
if (getRootCauseMessage(e).startsWith("5")) {
|
||||
localFolder.moveMessages(new Message[] { message }, (LocalFolder) localStore.getFolder(account.getDraftsFolderName()));
|
||||
localFolder.moveMessages(Collections.singletonList(message), (LocalFolder) localStore.getFolder(account.getDraftsFolderName()));
|
||||
}
|
||||
|
||||
notifyUserIfCertificateProblem(mApplication, e, account, false);
|
||||
@ -3679,14 +3657,14 @@ public class MessagingController implements Runnable {
|
||||
|
||||
// Collect accounts that belong to the search
|
||||
String[] accountUuids = search.getAccountUuids();
|
||||
Account[] accounts;
|
||||
List<Account> accounts;
|
||||
if (search.searchAllAccounts()) {
|
||||
accounts = preferences.getAccounts();
|
||||
} else {
|
||||
accounts = new Account[accountUuids.length];
|
||||
accounts = new ArrayList<Account>(accountUuids.length);
|
||||
for (int i = 0, len = accountUuids.length; i < len; i++) {
|
||||
String accountUuid = accountUuids[i];
|
||||
accounts[i] = preferences.getAccount(accountUuid);
|
||||
accounts.set(i, preferences.getAccount(accountUuid));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3895,8 +3873,8 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
Message[] messages = localSrcFolder.getMessages(uids.toArray(EMPTY_STRING_ARRAY), null);
|
||||
if (messages.length > 0) {
|
||||
List<? extends Message> messages = localSrcFolder.getMessages(uids.toArray(EMPTY_STRING_ARRAY), null);
|
||||
if (messages.size() > 0) {
|
||||
Map<String, Message> origUidMap = new HashMap<String, Message>();
|
||||
|
||||
for (Message message : messages) {
|
||||
@ -3905,7 +3883,7 @@ public class MessagingController implements Runnable {
|
||||
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "moveOrCopyMessageSynchronous: source folder = " + srcFolder
|
||||
+ ", " + messages.length + " messages, " + ", destination folder = " + destFolder + ", isCopy = " + isCopy);
|
||||
+ ", " + messages.size() + " messages, " + ", destination folder = " + destFolder + ", isCopy = " + isCopy);
|
||||
|
||||
if (isCopy) {
|
||||
FetchProfile fp = new FetchProfile();
|
||||
@ -4014,7 +3992,7 @@ public class MessagingController implements Runnable {
|
||||
List<Message> messagesToDelete = collectMessagesInThreads(account, messages);
|
||||
|
||||
deleteMessagesSynchronous(account, folderName,
|
||||
messagesToDelete.toArray(EMPTY_MESSAGE_ARRAY), null);
|
||||
messagesToDelete, null);
|
||||
} catch (MessagingException e) {
|
||||
Log.e(K9.LOG_TAG, "Something went wrong while deleting threads", e);
|
||||
}
|
||||
@ -4031,8 +4009,9 @@ public class MessagingController implements Runnable {
|
||||
long rootId = localMessage.getRootId();
|
||||
long threadId = (rootId == -1) ? localMessage.getThreadId() : rootId;
|
||||
|
||||
Message[] messagesInThread = localStore.getMessagesInThread(threadId);
|
||||
Collections.addAll(messagesInThreads, messagesInThread);
|
||||
List<? extends Message> messagesInThread = localStore.getMessagesInThread(threadId);
|
||||
|
||||
messagesInThreads.addAll(messagesInThread);
|
||||
}
|
||||
|
||||
return messagesInThreads;
|
||||
@ -4050,7 +4029,7 @@ public class MessagingController implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
deleteMessagesSynchronous(account, folder.getName(),
|
||||
accountMessages.toArray(EMPTY_MESSAGE_ARRAY), listener);
|
||||
accountMessages, listener);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -4059,7 +4038,7 @@ public class MessagingController implements Runnable {
|
||||
|
||||
}
|
||||
|
||||
private void deleteMessagesSynchronous(final Account account, final String folder, final Message[] messages,
|
||||
private void deleteMessagesSynchronous(final Account account, final String folder, final List<? extends Message> messages,
|
||||
MessagingListener listener) {
|
||||
Folder localFolder = null;
|
||||
Folder localTrashFolder = null;
|
||||
@ -4079,7 +4058,7 @@ public class MessagingController implements Runnable {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "Deleting messages in trash folder or trash set to -None-, not copying");
|
||||
|
||||
localFolder.setFlags(messages, new Flag[] { Flag.DELETED }, true);
|
||||
localFolder.setFlags(messages, Collections.singleton(Flag.DELETED), true);
|
||||
} else {
|
||||
localTrashFolder = localStore.getFolder(account.getTrashFolderName());
|
||||
if (!localTrashFolder.exists()) {
|
||||
@ -4147,10 +4126,10 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private String[] getUidsFromMessages(Message[] messages) {
|
||||
String[] uids = new String[messages.length];
|
||||
for (int i = 0; i < messages.length; i++) {
|
||||
uids[i] = messages[i].getUid();
|
||||
private String[] getUidsFromMessages(List <? extends Message> messages) {
|
||||
String[] uids = new String[messages.size()];
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
uids[i] = messages.get(i).getUid();
|
||||
}
|
||||
return uids;
|
||||
}
|
||||
@ -4162,7 +4141,7 @@ public class MessagingController implements Runnable {
|
||||
try {
|
||||
if (remoteFolder.exists()) {
|
||||
remoteFolder.open(Folder.OPEN_MODE_RW);
|
||||
remoteFolder.setFlags(new Flag [] { Flag.DELETED }, true);
|
||||
remoteFolder.setFlags(Collections.singleton(Flag.DELETED), true);
|
||||
if (Account.EXPUNGE_IMMEDIATELY.equals(account.getExpungePolicy())) {
|
||||
remoteFolder.expunge();
|
||||
}
|
||||
@ -4193,7 +4172,7 @@ public class MessagingController implements Runnable {
|
||||
if (isTrashLocalOnly) {
|
||||
localFolder.clearAllMessages();
|
||||
} else {
|
||||
localFolder.setFlags(new Flag[] { Flag.DELETED }, true);
|
||||
localFolder.setFlags(Collections.singleton(Flag.DELETED), true);
|
||||
}
|
||||
|
||||
for (MessagingListener l : getListeners()) {
|
||||
@ -4863,7 +4842,8 @@ public class MessagingController implements Runnable {
|
||||
|
||||
String accountDescr = (account.getDescription() != null) ?
|
||||
account.getDescription() : account.getEmail();
|
||||
final ArrayList<MessageReference> allRefs = data.getAllMessageRefs();
|
||||
final ArrayList<MessageReference> allRefs = new ArrayList<MessageReference>();
|
||||
data.supplyAllMessageRefs(allRefs);
|
||||
|
||||
if (platformSupportsExtendedNotifications() && !privacyModeEnabled) {
|
||||
if (newMessages > 1) {
|
||||
@ -5030,7 +5010,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
|
||||
private boolean skipAccountsInBackStack(Context context) {
|
||||
return Preferences.getPreferences(context).getAccounts().length == 1;
|
||||
return Preferences.getPreferences(context).getAccounts().size() == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -5112,9 +5092,7 @@ public class MessagingController implements Runnable {
|
||||
}
|
||||
|
||||
// Save the message to the store.
|
||||
localFolder.appendMessages(new Message[] {
|
||||
message
|
||||
});
|
||||
localFolder.appendMessages(Collections.singletonList(message));
|
||||
// Fetch the message back from the store. This is the Message that's returned to the caller.
|
||||
localMessage = localFolder.getMessage(message.getUid());
|
||||
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
|
||||
@ -5424,7 +5402,7 @@ public class MessagingController implements Runnable {
|
||||
return taccount.getDescription() + ":" + tfolderName;
|
||||
}
|
||||
static class MemorizingListener extends MessagingListener {
|
||||
HashMap<String, Memory> memories = new HashMap<String, Memory>(31);
|
||||
Map<String, Memory> memories = new HashMap<String, Memory>(31);
|
||||
|
||||
Memory getMemory(Account account, String folderName) {
|
||||
Memory memory = memories.get(getMemoryKey(account, folderName));
|
||||
|
@ -11,8 +11,8 @@ import com.fsck.k9.mail.Folder;
|
||||
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.PushReceiver;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||
import com.fsck.k9.mail.store.local.LocalStore;
|
||||
import com.fsck.k9.service.SleepService;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -33,7 +33,7 @@ public class MessagingListener {
|
||||
|
||||
public void listFoldersStarted(Account account) {}
|
||||
|
||||
public void listFolders(Account account, Folder[] folders) {}
|
||||
public void listFolders(Account account, List<? extends Folder> folders) {}
|
||||
|
||||
public void listFoldersFinished(Account account) {}
|
||||
|
||||
|
@ -88,8 +88,8 @@ import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.Folder;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||
import com.fsck.k9.mail.store.local.LocalStore;
|
||||
import com.fsck.k9.provider.EmailProvider;
|
||||
import com.fsck.k9.provider.EmailProvider.MessageColumns;
|
||||
import com.fsck.k9.provider.EmailProvider.SpecialColumns;
|
||||
@ -960,16 +960,16 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
accountUuids[0].equals(SearchSpecification.ALL_ACCOUNTS)) {
|
||||
mAllAccounts = true;
|
||||
|
||||
Account[] accounts = mPreferences.getAccounts();
|
||||
List<Account> accounts = mPreferences.getAccounts();
|
||||
|
||||
mAccountUuids = new String[accounts.length];
|
||||
for (int i = 0, len = accounts.length; i < len; i++) {
|
||||
mAccountUuids[i] = accounts[i].getUuid();
|
||||
mAccountUuids = new String[accounts.size()];
|
||||
for (int i = 0, len = accounts.size(); i < len; i++) {
|
||||
mAccountUuids[i] = accounts.get(i).getUuid();
|
||||
}
|
||||
|
||||
if (mAccountUuids.length == 1) {
|
||||
mSingleAccountMode = true;
|
||||
mAccount = accounts[0];
|
||||
mAccount = accounts.get(0);
|
||||
}
|
||||
} else {
|
||||
mAccountUuids = accountUuids;
|
||||
@ -1084,11 +1084,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
mController.addListener(mListener);
|
||||
|
||||
//Cancel pending new mail notifications when we open an account
|
||||
Account[] accountsWithNotification;
|
||||
List<Account> accountsWithNotification;
|
||||
|
||||
Account account = mAccount;
|
||||
if (account != null) {
|
||||
accountsWithNotification = new Account[] { account };
|
||||
accountsWithNotification = Collections.singletonList(account);
|
||||
} else {
|
||||
accountsWithNotification = mPreferences.getAccounts();
|
||||
}
|
||||
@ -1814,7 +1814,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
}
|
||||
|
||||
List<String> folderNames = mSearch.getFolderNames();
|
||||
return (folderNames.size() == 0 || folderNames.contains(folder));
|
||||
return (folderNames.isEmpty() || folderNames.contains(folder));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2362,7 +2362,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
}
|
||||
|
||||
private void setFlagForSelected(final Flag flag, final boolean newState) {
|
||||
if (mSelected.size() == 0) {
|
||||
if (mSelected.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2586,7 +2586,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
private boolean checkCopyOrMovePossible(final List<Message> messages,
|
||||
final FolderOperation operation) {
|
||||
|
||||
if (messages.size() == 0) {
|
||||
if (messages.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2995,8 +2995,8 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
public ArrayList<MessageReference> getMessageReferences() {
|
||||
ArrayList<MessageReference> messageRefs = new ArrayList<MessageReference>();
|
||||
public List<MessageReference> getMessageReferences() {
|
||||
List<MessageReference> messageRefs = new ArrayList<MessageReference>();
|
||||
|
||||
for (int i = 0, len = mAdapter.getCount(); i < len; i++) {
|
||||
Cursor cursor = (Cursor) mAdapter.getItem(i);
|
||||
@ -3515,7 +3515,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
}
|
||||
|
||||
private void cleanupSelected(Cursor cursor) {
|
||||
if (mSelected.size() == 0) {
|
||||
if (mSelected.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3534,7 +3534,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
|
||||
* Starts or finishes the action mode when necessary.
|
||||
*/
|
||||
private void resetActionMode() {
|
||||
if (mSelected.size() == 0) {
|
||||
if (mSelected.isEmpty()) {
|
||||
if (mActionMode != null) {
|
||||
mActionMode.finish();
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Part;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
||||
import com.fsck.k9.mail.store.local.LocalMessage;
|
||||
import com.fsck.k9.view.AttachmentView;
|
||||
import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback;
|
||||
import com.fsck.k9.view.MessageHeader;
|
||||
@ -369,7 +369,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
|
||||
if (mMessage != null) {
|
||||
boolean newState = !mMessage.isSet(Flag.FLAGGED);
|
||||
mController.setFlag(mAccount, mMessage.getFolder().getName(),
|
||||
new Message[] { mMessage }, Flag.FLAGGED, newState);
|
||||
Collections.singletonList(mMessage), Flag.FLAGGED, newState);
|
||||
mMessageView.setHeaders(mMessage, mAccount);
|
||||
}
|
||||
}
|
||||
@ -485,7 +485,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
|
||||
public void onToggleRead() {
|
||||
if (mMessage != null) {
|
||||
mController.setFlag(mAccount, mMessage.getFolder().getName(),
|
||||
new Message[] { mMessage }, Flag.SEEN, !mMessage.isSet(Flag.SEEN));
|
||||
Collections.singletonList(mMessage), Flag.SEEN, !mMessage.isSet(Flag.SEEN));
|
||||
mMessageView.setHeaders(mMessage, mAccount);
|
||||
String subject = mMessage.getSubject();
|
||||
displayMessageSubject(subject);
|
||||
|
@ -13,6 +13,7 @@ import com.fsck.k9.K9;
|
||||
import com.fsck.k9.mail.Address;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class to access the contacts stored on the device.
|
||||
@ -276,7 +277,7 @@ public class Contacts {
|
||||
*/
|
||||
public ContactItem extractInfoFromContactPickerIntent(final Intent intent) {
|
||||
Cursor cursor = null;
|
||||
ArrayList<String> email = new ArrayList<String>();
|
||||
List<String> email = new ArrayList<String>();
|
||||
|
||||
try {
|
||||
Uri result = intent.getData();
|
||||
@ -301,7 +302,7 @@ public class Contacts {
|
||||
}
|
||||
|
||||
// Return 'null' if no email addresses have been found
|
||||
if (email.size() == 0) {
|
||||
if (email.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
33
src/com/fsck/k9/helper/UrlEncodingHelper.java
Normal file
33
src/com/fsck/k9/helper/UrlEncodingHelper.java
Normal file
@ -0,0 +1,33 @@
|
||||
package com.fsck.k9.helper;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* Wraps the java.net.URLDecoder to avoid unhelpful checked exceptions.
|
||||
*/
|
||||
public class UrlEncodingHelper {
|
||||
|
||||
public static String decodeUtf8(String s) {
|
||||
try {
|
||||
return URLDecoder.decode(s, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
/*
|
||||
* This is impossible, UTF-8 is always supported
|
||||
*/
|
||||
throw new RuntimeException("UTF-8 not found");
|
||||
}
|
||||
}
|
||||
|
||||
public static String encodeUtf8(String s) {
|
||||
try {
|
||||
return URLEncoder.encode(s, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
/*
|
||||
* This is impossible, UTF-8 is always supported
|
||||
*/
|
||||
throw new RuntimeException("UTF-8 not found");
|
||||
}
|
||||
}
|
||||
}
|
@ -20,10 +20,13 @@ import com.fsck.k9.mail.filter.Base64;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -102,6 +105,22 @@ public class Utility {
|
||||
return TextUtils.join(String.valueOf(separator), parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines the given Objects into a single String using
|
||||
* each Object's toString() method and the separator character
|
||||
* between each part.
|
||||
*
|
||||
* @param parts
|
||||
* @param separator
|
||||
* @return new String
|
||||
*/
|
||||
public static String combine(Iterable<?> parts, char separator) {
|
||||
if (parts == null) {
|
||||
return null;
|
||||
}
|
||||
return TextUtils.join(String.valueOf(separator), parts);
|
||||
}
|
||||
|
||||
public static String base64Decode(String encoded) {
|
||||
if (encoded == null) {
|
||||
return null;
|
||||
@ -189,8 +208,8 @@ public class Utility {
|
||||
* hundreds of times in places that slow down the UI, so it helps.
|
||||
*/
|
||||
public static String fastUrlDecode(String s) {
|
||||
try {
|
||||
byte[] bytes = s.getBytes("UTF-8");
|
||||
|
||||
byte[] bytes = s.getBytes(Charset.forName("UTF-8"));
|
||||
byte ch;
|
||||
int length = 0;
|
||||
for (int i = 0, count = bytes.length; i < count; i++) {
|
||||
@ -213,10 +232,8 @@ public class Utility {
|
||||
}
|
||||
length++;
|
||||
}
|
||||
return new String(bytes, 0, length, "UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
return null;
|
||||
}
|
||||
return new String(bytes, 0, length, Charset.forName("UTF-8"));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@ -706,4 +723,5 @@ public class Utility {
|
||||
}
|
||||
return sMainThreadHandler;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -44,9 +44,9 @@ public class Address {
|
||||
*/
|
||||
private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[0];
|
||||
|
||||
String mAddress;
|
||||
private String mAddress;
|
||||
|
||||
String mPersonal;
|
||||
private String mPersonal;
|
||||
|
||||
|
||||
public Address(Address address) {
|
||||
@ -315,7 +315,7 @@ public class Address {
|
||||
if (addressList == null) {
|
||||
return new Address[] { };
|
||||
}
|
||||
ArrayList<Address> addresses = new ArrayList<Address>();
|
||||
List<Address> addresses = new ArrayList<Address>();
|
||||
int length = addressList.length();
|
||||
int pairStartIndex = 0;
|
||||
int pairEndIndex = 0;
|
||||
|
@ -8,7 +8,21 @@ import java.io.OutputStream;
|
||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
||||
|
||||
public interface Body {
|
||||
/**
|
||||
* Returns the raw data of the body, without transfer encoding etc applied.
|
||||
* TODO perhaps it would be better to have an intermediate "simple part" class where this method could reside
|
||||
* because it makes no sense for multiparts
|
||||
*/
|
||||
public InputStream getInputStream() throws MessagingException;
|
||||
|
||||
/**
|
||||
* Sets the content transfer encoding (7bit, 8bit, quoted-printable or base64).
|
||||
*/
|
||||
public void setEncoding(String encoding) throws UnavailableStorageException, MessagingException;
|
||||
|
||||
/**
|
||||
* Writes the body's data to the given {@link OutputStream}.
|
||||
* The written data is transfer encoded (e.g. transformed to Base64 when needed).
|
||||
*/
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException;
|
||||
}
|
||||
|
@ -14,5 +14,6 @@ public abstract class BodyPart implements Part {
|
||||
|
||||
public abstract void setEncoding(String encoding) throws MessagingException;
|
||||
|
||||
@Override
|
||||
public abstract void setUsing7bitTransport() throws MessagingException;
|
||||
}
|
||||
|
@ -13,14 +13,8 @@ public class CertificateChainException extends CertificateException {
|
||||
private static final long serialVersionUID = 1103894512106650107L;
|
||||
private X509Certificate[] mCertChain;
|
||||
|
||||
public CertificateChainException(String msg, X509Certificate[] chain) {
|
||||
super(msg);
|
||||
setCertChain(chain);
|
||||
}
|
||||
|
||||
public CertificateChainException(CertificateException ce,
|
||||
X509Certificate[] chain) {
|
||||
super.initCause(ce);
|
||||
public CertificateChainException(String msg, X509Certificate[] chain, Throwable cause) {
|
||||
super(msg, cause);
|
||||
setCertChain(chain);
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@ public interface CompositeBody extends Body {
|
||||
* @throws MessagingException
|
||||
*
|
||||
*/
|
||||
|
||||
public abstract void setUsing7bitTransport() throws MessagingException;
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package com.fsck.k9.mail;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import android.util.Log;
|
||||
import com.fsck.k9.Account;
|
||||
@ -92,7 +94,7 @@ public abstract class Folder {
|
||||
* @return List of messages
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public abstract Message[] getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener) throws MessagingException;
|
||||
public abstract List<? extends Message> getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener) throws MessagingException;
|
||||
|
||||
/**
|
||||
* Fetches the given list of messages. The specified listener is notified as
|
||||
@ -102,36 +104,36 @@ public abstract class Folder {
|
||||
* @param listener Listener to notify as we download messages.
|
||||
* @return List of messages
|
||||
*/
|
||||
public abstract Message[] getMessages(MessageRetrievalListener listener) throws MessagingException;
|
||||
public abstract List<? extends Message> getMessages(MessageRetrievalListener listener) throws MessagingException;
|
||||
|
||||
public Message[] getMessages(MessageRetrievalListener listener, boolean includeDeleted) throws MessagingException {
|
||||
public List<? extends Message> getMessages(MessageRetrievalListener listener, boolean includeDeleted) throws MessagingException {
|
||||
return getMessages(listener);
|
||||
}
|
||||
|
||||
public abstract Message[] getMessages(String[] uids, MessageRetrievalListener listener)
|
||||
public abstract List<? extends Message> getMessages(String[] uids, MessageRetrievalListener listener)
|
||||
throws MessagingException;
|
||||
|
||||
public abstract Map<String, String> appendMessages(Message[] messages) throws MessagingException;
|
||||
public abstract Map<String, String> appendMessages(List<? extends Message> messages) throws MessagingException;
|
||||
|
||||
public Map<String, String> copyMessages(Message[] msgs, Folder folder) throws MessagingException {
|
||||
public Map<String, String> copyMessages(List<? extends Message> msgs, Folder folder) throws MessagingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Map<String, String> moveMessages(Message[] msgs, Folder folder) throws MessagingException {
|
||||
public Map<String, String> moveMessages(List<? extends Message> msgs, Folder folder) throws MessagingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void delete(Message[] msgs, String trashFolderName) throws MessagingException {
|
||||
public void delete(List<? extends Message> msgs, String trashFolderName) throws MessagingException {
|
||||
for (Message message : msgs) {
|
||||
Message myMessage = getMessage(message.getUid());
|
||||
myMessage.delete(trashFolderName);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void setFlags(Message[] messages, Flag[] flags, boolean value)
|
||||
public abstract void setFlags(List<? extends Message> messages, Set<Flag> flags, boolean value)
|
||||
throws MessagingException;
|
||||
|
||||
public abstract void setFlags(Flag[] flags, boolean value) throws MessagingException;
|
||||
public abstract void setFlags(Set<Flag> flags, boolean value) throws MessagingException;
|
||||
|
||||
public abstract String getUidFromMessageId(Message message) throws MessagingException;
|
||||
|
||||
@ -146,7 +148,7 @@ public abstract class Folder {
|
||||
* @param listener Listener to notify as we fetch messages.
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public abstract void fetch(Message[] messages, FetchProfile fp,
|
||||
public abstract void fetch(List<? extends Message> messages, FetchProfile fp,
|
||||
MessageRetrievalListener listener) throws MessagingException;
|
||||
|
||||
public void fetchPart(Message message, Part part,
|
||||
@ -243,7 +245,7 @@ public abstract class Folder {
|
||||
return mAccount;
|
||||
}
|
||||
|
||||
public List<Message> search(String queryString, final Flag[] requiredFlags, final Flag[] forbiddenFlags)
|
||||
public List<Message> search(String queryString, final Set<Flag> requiredFlags, final Set<Flag> forbiddenFlags)
|
||||
throws MessagingException {
|
||||
throw new MessagingException("K-9 does not support searches on this folder type");
|
||||
}
|
||||
|
@ -2,7 +2,10 @@
|
||||
package com.fsck.k9.mail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@ -16,7 +19,6 @@ import com.fsck.k9.mail.store.UnavailableStorageException;
|
||||
|
||||
|
||||
public abstract class Message implements Part, CompositeBody {
|
||||
private static final Flag[] EMPTY_FLAG_ARRAY = new Flag[0];
|
||||
|
||||
private MessageReference mReference = null;
|
||||
|
||||
@ -26,9 +28,9 @@ public abstract class Message implements Part, CompositeBody {
|
||||
|
||||
protected String mUid;
|
||||
|
||||
protected Set<Flag> mFlags = new HashSet<Flag>();
|
||||
private Set<Flag> mFlags = EnumSet.noneOf(Flag.class);
|
||||
|
||||
protected Date mInternalDate;
|
||||
private Date mInternalDate;
|
||||
|
||||
protected Folder mFolder;
|
||||
|
||||
@ -45,6 +47,7 @@ public abstract class Message implements Part, CompositeBody {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !(o instanceof Message)) {
|
||||
@ -123,20 +126,27 @@ public abstract class Message implements Part, CompositeBody {
|
||||
|
||||
public abstract void setReferences(String references) throws MessagingException;
|
||||
|
||||
@Override
|
||||
public abstract Body getBody();
|
||||
|
||||
@Override
|
||||
public abstract String getContentType() throws MessagingException;
|
||||
|
||||
@Override
|
||||
public abstract void addHeader(String name, String value) throws MessagingException;
|
||||
|
||||
@Override
|
||||
public abstract void setHeader(String name, String value) throws MessagingException;
|
||||
|
||||
@Override
|
||||
public abstract String[] getHeader(String name) throws MessagingException;
|
||||
|
||||
public abstract Set<String> getHeaderNames() throws UnavailableStorageException;
|
||||
|
||||
@Override
|
||||
public abstract void removeHeader(String name) throws MessagingException;
|
||||
|
||||
@Override
|
||||
public abstract void setBody(Body body) throws MessagingException;
|
||||
|
||||
public abstract long getId();
|
||||
@ -144,6 +154,8 @@ public abstract class Message implements Part, CompositeBody {
|
||||
public abstract String getPreview();
|
||||
public abstract boolean hasAttachments();
|
||||
|
||||
public abstract int getSize();
|
||||
|
||||
/*
|
||||
* calculateContentPreview
|
||||
* Takes a plain text message body as a string.
|
||||
@ -198,8 +210,8 @@ public abstract class Message implements Part, CompositeBody {
|
||||
/*
|
||||
* TODO Refactor Flags at some point to be able to store user defined flags.
|
||||
*/
|
||||
public Flag[] getFlags() {
|
||||
return mFlags.toArray(EMPTY_FLAG_ARRAY);
|
||||
public Set<Flag> getFlags() {
|
||||
return Collections.unmodifiableSet(mFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -223,7 +235,7 @@ public abstract class Message implements Part, CompositeBody {
|
||||
* @param flags
|
||||
* @param set
|
||||
*/
|
||||
public void setFlags(Flag[] flags, boolean set) throws MessagingException {
|
||||
public void setFlags(final Set<Flag> flags, boolean set) throws MessagingException {
|
||||
for (Flag flag : flags) {
|
||||
setFlag(flag, set);
|
||||
}
|
||||
@ -236,6 +248,7 @@ public abstract class Message implements Part, CompositeBody {
|
||||
|
||||
public void destroy() throws MessagingException {}
|
||||
|
||||
@Override
|
||||
public abstract void setEncoding(String encoding) throws UnavailableStorageException, MessagingException;
|
||||
|
||||
public abstract void setCharset(String charset) throws MessagingException;
|
||||
@ -279,7 +292,7 @@ public abstract class Message implements Part, CompositeBody {
|
||||
destination.mReference = mReference;
|
||||
|
||||
// mFlags contents can change during the object lifetime, so copy the Set
|
||||
destination.mFlags = new HashSet<Flag>(mFlags);
|
||||
destination.mFlags = EnumSet.copyOf(mFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -293,6 +306,8 @@ public abstract class Message implements Part, CompositeBody {
|
||||
* for more information.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public abstract Message clone();
|
||||
@Override
|
||||
public abstract void setUsing7bitTransport() throws MessagingException;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ package com.fsck.k9.mail;
|
||||
public class MessagingException extends Exception {
|
||||
public static final long serialVersionUID = -1;
|
||||
|
||||
boolean permanentFailure = false;
|
||||
private boolean permanentFailure = false;
|
||||
|
||||
public MessagingException(String message) {
|
||||
super(message);
|
||||
@ -28,9 +28,9 @@ public class MessagingException extends Exception {
|
||||
return permanentFailure;
|
||||
}
|
||||
|
||||
//TODO setters in Exception are bad style, remove (it's nearly unused anyway)
|
||||
public void setPermanentFailure(boolean permanentFailure) {
|
||||
this.permanentFailure = permanentFailure;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
package com.fsck.k9.mail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.james.mime4j.util.MimeUtil;
|
||||
|
||||
@ -9,26 +11,25 @@ import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.internet.TextBody;
|
||||
|
||||
public abstract class Multipart implements CompositeBody {
|
||||
protected Part mParent;
|
||||
private Part mParent;
|
||||
|
||||
protected ArrayList<BodyPart> mParts = new ArrayList<BodyPart>();
|
||||
private final List<BodyPart> mParts = new ArrayList<BodyPart>();
|
||||
|
||||
protected String mContentType;
|
||||
private String mContentType;
|
||||
|
||||
public void addBodyPart(BodyPart part) {
|
||||
mParts.add(part);
|
||||
part.setParent(this);
|
||||
}
|
||||
|
||||
public void addBodyPart(BodyPart part, int index) {
|
||||
mParts.add(index, part);
|
||||
part.setParent(this);
|
||||
}
|
||||
|
||||
public BodyPart getBodyPart(int index) {
|
||||
return mParts.get(index);
|
||||
}
|
||||
|
||||
public List<BodyPart> getBodyParts() {
|
||||
return Collections.unmodifiableList(mParts);
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return mContentType;
|
||||
}
|
||||
@ -37,16 +38,6 @@ public abstract class Multipart implements CompositeBody {
|
||||
return mParts.size();
|
||||
}
|
||||
|
||||
public boolean removeBodyPart(BodyPart part) {
|
||||
part.setParent(null);
|
||||
return mParts.remove(part);
|
||||
}
|
||||
|
||||
public void removeBodyPart(int index) {
|
||||
mParts.get(index).setParent(null);
|
||||
mParts.remove(index);
|
||||
}
|
||||
|
||||
public Part getParent() {
|
||||
return mParent;
|
||||
}
|
||||
@ -55,6 +46,7 @@ public abstract class Multipart implements CompositeBody {
|
||||
this.mParent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncoding(String encoding) throws MessagingException {
|
||||
if (!MimeUtil.ENC_7BIT.equalsIgnoreCase(encoding)
|
||||
&& !MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) {
|
||||
|
@ -21,8 +21,6 @@ public interface Part {
|
||||
|
||||
public String[] getHeader(String name) throws MessagingException;
|
||||
|
||||
public int getSize();
|
||||
|
||||
public boolean isMimeType(String mimeType) throws MessagingException;
|
||||
|
||||
public String getMimeType() throws MessagingException;
|
||||
@ -42,5 +40,6 @@ public interface Part {
|
||||
* @throws MessagingException
|
||||
*
|
||||
*/
|
||||
//TODO perhaps it would be clearer to use a flag "force7bit" in writeTo
|
||||
public abstract void setUsing7bitTransport() throws MessagingException;
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ package com.fsck.k9.mail;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
@ -12,9 +14,9 @@ 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.LocalStore;
|
||||
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;
|
||||
|
||||
@ -33,19 +35,19 @@ public abstract class Store {
|
||||
/**
|
||||
* Remote stores indexed by Uri.
|
||||
*/
|
||||
private static HashMap<String, Store> sStores = new HashMap<String, Store>();
|
||||
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 ConcurrentHashMap<String, Store> sLocalStores = new ConcurrentHashMap<String, Store>();
|
||||
private static ConcurrentMap<String, Store> sLocalStores = new ConcurrentHashMap<String, Store>();
|
||||
|
||||
/**
|
||||
* Lock objects indexed by account UUID.
|
||||
*
|
||||
* @see #getLocalInstance(Account, Application)
|
||||
*/
|
||||
private static ConcurrentHashMap<String, Object> sAccountLocks = new ConcurrentHashMap<String, Object>();
|
||||
private static ConcurrentMap<String, Object> sAccountLocks = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
/**
|
||||
* Get an instance of a remote mail store.
|
||||
@ -242,7 +244,7 @@ public abstract class Store {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void sendMessages(Message[] messages) throws MessagingException {
|
||||
public void sendMessages(List<? extends Message> messages) throws MessagingException {
|
||||
}
|
||||
|
||||
public Pusher getPusher(PushReceiver receiver) {
|
||||
|
@ -17,8 +17,8 @@
|
||||
|
||||
package com.fsck.k9.mail.filter;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* Provides Base64 encoding and decoding as defined by RFC 2045.
|
||||
@ -225,12 +225,8 @@ public class Base64 {
|
||||
}
|
||||
this.decodeSize = encodeSize - 1;
|
||||
if (containsBase64Byte(lineSeparator)) {
|
||||
String sep;
|
||||
try {
|
||||
sep = new String(lineSeparator, "UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
sep = new String(lineSeparator);
|
||||
}
|
||||
String sep = new String(lineSeparator, Charset.forName("UTF-8"));
|
||||
|
||||
throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]");
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,8 @@ import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.james.mime4j.codec.Base64InputStream;
|
||||
import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
|
||||
import org.apache.james.mime4j.util.CharsetUtil;
|
||||
@ -30,12 +31,7 @@ public class DecoderUtil {
|
||||
* @return the decoded string.
|
||||
*/
|
||||
private static String decodeB(String encodedWord, String charset) {
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = encodedWord.getBytes("US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return null;
|
||||
}
|
||||
byte[] bytes = encodedWord.getBytes(Charset.forName("US-ASCII"));
|
||||
|
||||
Base64InputStream is = new Base64InputStream(new ByteArrayInputStream(bytes));
|
||||
try {
|
||||
@ -68,12 +64,7 @@ public class DecoderUtil {
|
||||
}
|
||||
}
|
||||
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = sb.toString().getBytes("US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return null;
|
||||
}
|
||||
byte[] bytes = sb.toString().getBytes(Charset.forName("US-ASCII"));
|
||||
|
||||
QuotedPrintableInputStream is = new QuotedPrintableInputStream(new ByteArrayInputStream(bytes));
|
||||
try {
|
||||
|
@ -20,9 +20,8 @@ import org.apache.james.mime4j.util.MimeUtil;
|
||||
* Message.
|
||||
*/
|
||||
public class MimeBodyPart extends BodyPart {
|
||||
protected MimeHeader mHeader = new MimeHeader();
|
||||
protected Body mBody;
|
||||
protected int mSize;
|
||||
private final MimeHeader mHeader = new MimeHeader();
|
||||
private Body mBody;
|
||||
|
||||
public MimeBodyPart() throws MessagingException {
|
||||
this(null);
|
||||
@ -39,30 +38,36 @@ public class MimeBodyPart extends BodyPart {
|
||||
setBody(body);
|
||||
}
|
||||
|
||||
protected String getFirstHeader(String name) {
|
||||
private String getFirstHeader(String name) {
|
||||
return mHeader.getFirstHeader(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHeader(String name, String value) throws MessagingException {
|
||||
mHeader.addHeader(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeader(String name, String value) {
|
||||
mHeader.setHeader(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getHeader(String name) throws MessagingException {
|
||||
return mHeader.getHeader(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHeader(String name) throws MessagingException {
|
||||
mHeader.removeHeader(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Body getBody() {
|
||||
return mBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBody(Body body) throws MessagingException {
|
||||
this.mBody = body;
|
||||
if (body instanceof Multipart) {
|
||||
@ -94,15 +99,18 @@ public class MimeBodyPart extends BodyPart {
|
||||
setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType() throws MessagingException {
|
||||
String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
|
||||
return (contentType == null) ? "text/plain" : contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisposition() throws MessagingException {
|
||||
return getFirstHeader(MimeHeader.HEADER_CONTENT_DISPOSITION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentId() throws MessagingException {
|
||||
String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID);
|
||||
if (contentId == null) {
|
||||
@ -117,21 +125,20 @@ public class MimeBodyPart extends BodyPart {
|
||||
contentId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() throws MessagingException {
|
||||
return MimeUtility.getHeaderParameter(getContentType(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMimeType(String mimeType) throws MessagingException {
|
||||
return getMimeType().equalsIgnoreCase(mimeType);
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the MimeMessage out in MIME format.
|
||||
*/
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
|
||||
mHeader.writeTo(out);
|
||||
|
@ -36,7 +36,7 @@ public class MimeHeader {
|
||||
HEADER_ANDROID_ATTACHMENT_STORE_DATA
|
||||
};
|
||||
|
||||
protected ArrayList<Field> mFields = new ArrayList<Field>();
|
||||
private List<Field> mFields = new ArrayList<Field>();
|
||||
private String mCharset = null;
|
||||
|
||||
public void clear() {
|
||||
@ -72,7 +72,7 @@ public class MimeHeader {
|
||||
}
|
||||
|
||||
public String[] getHeader(String name) {
|
||||
ArrayList<String> values = new ArrayList<String>();
|
||||
List<String> values = new ArrayList<String>();
|
||||
for (Field field : mFields) {
|
||||
if (field.name.equalsIgnoreCase(name)) {
|
||||
values.add(field.value);
|
||||
@ -85,7 +85,7 @@ public class MimeHeader {
|
||||
}
|
||||
|
||||
public void removeHeader(String name) {
|
||||
ArrayList<Field> removeFields = new ArrayList<Field>();
|
||||
List<Field> removeFields = new ArrayList<Field>();
|
||||
for (Field field : mFields) {
|
||||
if (field.name.equalsIgnoreCase(name)) {
|
||||
removeFields.add(field);
|
||||
@ -119,7 +119,7 @@ public class MimeHeader {
|
||||
}
|
||||
|
||||
// encode non printable characters except LF/CR/TAB codes.
|
||||
public boolean hasToBeEncoded(String text) {
|
||||
private boolean hasToBeEncoded(String text) {
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if ((c < 0x20 || 0x7e < c) && // non printable
|
||||
@ -131,10 +131,10 @@ public class MimeHeader {
|
||||
return false;
|
||||
}
|
||||
|
||||
static class Field {
|
||||
final String name;
|
||||
private static class Field {
|
||||
private final String name;
|
||||
|
||||
final String value;
|
||||
private final String value;
|
||||
|
||||
public Field(String name, String value) {
|
||||
this.name = name;
|
||||
@ -153,6 +153,7 @@ public class MimeHeader {
|
||||
mCharset = charset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MimeHeader clone() {
|
||||
MimeHeader header = new MimeHeader();
|
||||
header.mCharset = mCharset;
|
||||
|
@ -41,7 +41,7 @@ import com.fsck.k9.K9;
|
||||
* RFC 2045 style headers.
|
||||
*/
|
||||
public class MimeMessage extends Message {
|
||||
protected MimeHeader mHeader = new MimeHeader();
|
||||
private MimeHeader mHeader = new MimeHeader();
|
||||
protected Address[] mFrom;
|
||||
protected Address[] mTo;
|
||||
protected Address[] mCc;
|
||||
@ -49,33 +49,19 @@ public class MimeMessage extends Message {
|
||||
protected Address[] mReplyTo;
|
||||
|
||||
protected String mMessageId;
|
||||
protected String[] mReferences;
|
||||
protected String[] mInReplyTo;
|
||||
private String[] mReferences;
|
||||
private String[] mInReplyTo;
|
||||
|
||||
protected Date mSentDate;
|
||||
protected SimpleDateFormat mDateFormat;
|
||||
private Date mSentDate;
|
||||
private SimpleDateFormat mDateFormat;
|
||||
|
||||
protected Body mBody;
|
||||
private Body mBody;
|
||||
protected int mSize;
|
||||
|
||||
public MimeMessage() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the given InputStream using Apache Mime4J to build a MimeMessage.
|
||||
* Nested messages will not be recursively parsed.
|
||||
*
|
||||
* @param in
|
||||
* @throws IOException
|
||||
* @throws MessagingException
|
||||
*
|
||||
* @see #MimeMessage(InputStream in, boolean recurse)
|
||||
*/
|
||||
public MimeMessage(InputStream in) throws IOException, MessagingException {
|
||||
parse(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given InputStream using Apache Mime4J to build a MimeMessage.
|
||||
*
|
||||
@ -88,11 +74,15 @@ public class MimeMessage extends Message {
|
||||
parse(in, recurse);
|
||||
}
|
||||
|
||||
protected void parse(InputStream in) throws IOException, MessagingException {
|
||||
/**
|
||||
* Parse the given InputStream using Apache Mime4J to build a MimeMessage.
|
||||
* Does not recurse through nested bodyparts.
|
||||
*/
|
||||
public final void parse(InputStream in) throws IOException, MessagingException {
|
||||
parse(in, false);
|
||||
}
|
||||
|
||||
protected void parse(InputStream in, boolean recurse) throws IOException, MessagingException {
|
||||
private void parse(InputStream in, boolean recurse) throws IOException, MessagingException {
|
||||
mHeader.clear();
|
||||
mFrom = null;
|
||||
mTo = null;
|
||||
@ -121,8 +111,8 @@ public class MimeMessage extends Message {
|
||||
try {
|
||||
parser.parse(new EOLConvertingInputStream(in));
|
||||
} catch (MimeException me) {
|
||||
//TODO wouldn't a MessagingException be better?
|
||||
throw new Error(me);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,20 +167,25 @@ public class MimeMessage extends Message {
|
||||
return (contentType == null) ? "text/plain" : contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisposition() throws MessagingException {
|
||||
return getFirstHeader(MimeHeader.HEADER_CONTENT_DISPOSITION);
|
||||
}
|
||||
@Override
|
||||
public String getContentId() throws MessagingException {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public String getMimeType() throws MessagingException {
|
||||
return MimeUtility.getHeaderParameter(getContentType(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMimeType(String mimeType) throws MessagingException {
|
||||
return getMimeType().equalsIgnoreCase(mimeType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return mSize;
|
||||
}
|
||||
@ -419,7 +414,7 @@ public class MimeMessage extends Message {
|
||||
}
|
||||
}
|
||||
|
||||
protected String getFirstHeader(String name) {
|
||||
private String getFirstHeader(String name) {
|
||||
return mHeader.getFirstHeader(name);
|
||||
}
|
||||
|
||||
@ -448,6 +443,7 @@ public class MimeMessage extends Message {
|
||||
return mHeader.getHeaderNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
|
||||
@ -459,6 +455,7 @@ public class MimeMessage extends Message {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws MessagingException {
|
||||
return null;
|
||||
}
|
||||
@ -482,7 +479,7 @@ public class MimeMessage extends Message {
|
||||
}
|
||||
}
|
||||
|
||||
class MimeMessageBuilder implements ContentHandler {
|
||||
private class MimeMessageBuilder implements ContentHandler {
|
||||
private final LinkedList<Object> stack = new LinkedList<Object>();
|
||||
|
||||
public MimeMessageBuilder() {
|
||||
@ -495,6 +492,7 @@ public class MimeMessage extends Message {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startMessage() {
|
||||
if (stack.isEmpty()) {
|
||||
stack.addFirst(MimeMessage.this);
|
||||
@ -510,22 +508,23 @@ public class MimeMessage extends Message {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endMessage() {
|
||||
expect(MimeMessage.class);
|
||||
stack.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startHeader() {
|
||||
expect(Part.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void endHeader() {
|
||||
expect(Part.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startMultipart(BodyDescriptor bd) {
|
||||
expect(Part.class);
|
||||
|
||||
@ -539,6 +538,7 @@ public class MimeMessage extends Message {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void body(BodyDescriptor bd, InputStream in) throws IOException {
|
||||
expect(Part.class);
|
||||
try {
|
||||
@ -550,10 +550,12 @@ public class MimeMessage extends Message {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endMultipart() {
|
||||
stack.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startBodyPart() {
|
||||
expect(MimeMultipart.class);
|
||||
|
||||
@ -566,21 +568,13 @@ public class MimeMessage extends Message {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endBodyPart() {
|
||||
expect(BodyPart.class);
|
||||
stack.removeFirst();
|
||||
}
|
||||
|
||||
public void epilogue(InputStream is) throws IOException {
|
||||
expect(MimeMultipart.class);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int b;
|
||||
while ((b = is.read()) != -1) {
|
||||
sb.append((char)b);
|
||||
}
|
||||
// ((Multipart) stack.peek()).setEpilogue(sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preamble(InputStream is) throws IOException {
|
||||
expect(MimeMultipart.class);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@ -589,9 +583,13 @@ public class MimeMessage extends Message {
|
||||
sb.append((char)b);
|
||||
}
|
||||
((MimeMultipart)stack.peek()).setPreamble(sb.toString());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void epilogue(InputStream is) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void raw(InputStream is) throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
@ -641,14 +639,17 @@ public class MimeMessage extends Message {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return Long.parseLong(mUid); //or maybe .mMessageId?
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreview() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAttachments() {
|
||||
return false;
|
||||
}
|
||||
|
@ -10,13 +10,11 @@ import java.util.Locale;
|
||||
import java.util.Random;
|
||||
|
||||
public class MimeMultipart extends Multipart {
|
||||
protected String mPreamble;
|
||||
private String mPreamble;
|
||||
|
||||
protected String mContentType;
|
||||
private String mContentType;
|
||||
|
||||
protected String mBoundary;
|
||||
|
||||
protected String mSubType;
|
||||
private final String mBoundary;
|
||||
|
||||
public MimeMultipart() throws MessagingException {
|
||||
mBoundary = generateBoundary();
|
||||
@ -26,7 +24,6 @@ public class MimeMultipart extends Multipart {
|
||||
public MimeMultipart(String contentType) throws MessagingException {
|
||||
this.mContentType = contentType;
|
||||
try {
|
||||
mSubType = MimeUtility.getHeaderParameter(contentType, null).split("/")[1];
|
||||
mBoundary = MimeUtility.getHeaderParameter(contentType, "boundary");
|
||||
if (mBoundary == null) {
|
||||
throw new MessagingException("MultiPart does not contain boundary: " + contentType);
|
||||
@ -62,10 +59,10 @@ public class MimeMultipart extends Multipart {
|
||||
}
|
||||
|
||||
public void setSubType(String subType) {
|
||||
this.mSubType = subType;
|
||||
mContentType = String.format("multipart/%s; boundary=\"%s\"", subType, mBoundary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
|
||||
|
||||
@ -74,20 +71,19 @@ public class MimeMultipart extends Multipart {
|
||||
writer.write("\r\n");
|
||||
}
|
||||
|
||||
if (mParts.isEmpty()) {
|
||||
if (getBodyParts().isEmpty()) {
|
||||
writer.write("--");
|
||||
writer.write(mBoundary);
|
||||
writer.write("\r\n");
|
||||
}
|
||||
|
||||
for (int i = 0, count = mParts.size(); i < count; i++) {
|
||||
BodyPart bodyPart = mParts.get(i);
|
||||
writer.write("--");
|
||||
writer.write(mBoundary);
|
||||
writer.write("\r\n");
|
||||
writer.flush();
|
||||
bodyPart.writeTo(out);
|
||||
writer.write("\r\n");
|
||||
} else {
|
||||
for (BodyPart bodyPart : getBodyParts()) {
|
||||
writer.write("--");
|
||||
writer.write(mBoundary);
|
||||
writer.write("\r\n");
|
||||
writer.flush();
|
||||
bodyPart.writeTo(out);
|
||||
writer.write("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
writer.write("--");
|
||||
@ -96,13 +92,14 @@ public class MimeMultipart extends Multipart {
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws MessagingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsing7bitTransport() throws MessagingException {
|
||||
for (BodyPart part : mParts) {
|
||||
for (BodyPart part : getBodyParts()) {
|
||||
part.setUsing7bitTransport();
|
||||
}
|
||||
}
|
||||
|
@ -927,11 +927,7 @@ public class MimeUtility {
|
||||
return s.replaceAll("\r|\n", "");
|
||||
}
|
||||
|
||||
public static String decode(String s) {
|
||||
return decode(s, null);
|
||||
}
|
||||
|
||||
public static String decode(String s, Message message) {
|
||||
private static String decode(String s, Message message) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
@ -993,8 +989,7 @@ public class MimeUtility {
|
||||
throws MessagingException {
|
||||
if (part.getBody() instanceof Multipart) {
|
||||
Multipart multipart = (Multipart)part.getBody();
|
||||
for (int i = 0, count = multipart.getCount(); i < count; i++) {
|
||||
BodyPart bodyPart = multipart.getBodyPart(i);
|
||||
for (BodyPart bodyPart : multipart.getBodyParts()) {
|
||||
Part ret = findFirstPartByMimeType(bodyPart, mimeType);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
@ -1006,28 +1001,6 @@ public class MimeUtility {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Part findPartByContentId(Part part, String contentId) throws Exception {
|
||||
if (part.getBody() instanceof Multipart) {
|
||||
Multipart multipart = (Multipart)part.getBody();
|
||||
for (int i = 0, count = multipart.getCount(); i < count; i++) {
|
||||
BodyPart bodyPart = multipart.getBodyPart(i);
|
||||
Part ret = findPartByContentId(bodyPart, contentId);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
String[] header = part.getHeader(MimeHeader.HEADER_CONTENT_ID);
|
||||
if (header != null) {
|
||||
for (String s : header) {
|
||||
if (s.equals(contentId)) {
|
||||
return part;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the Part's body and returns a String based on any charset conversion that needed
|
||||
* to be done. Note, this <b>does not</b> return a text representation of HTML.
|
||||
@ -1468,7 +1441,7 @@ public class MimeUtility {
|
||||
* @throws MessagingException
|
||||
* In case of an error.
|
||||
*/
|
||||
public static List<Viewable> getViewables(Part part, List<Part> attachments) throws MessagingException {
|
||||
private static List<Viewable> getViewables(Part part, List<Part> attachments) throws MessagingException {
|
||||
List<Viewable> viewables = new ArrayList<Viewable>();
|
||||
|
||||
Body body = part.getBody();
|
||||
@ -1490,9 +1463,7 @@ public class MimeUtility {
|
||||
}
|
||||
} else {
|
||||
// For all other multipart parts we recurse to grab all viewable children.
|
||||
int childCount = multipart.getCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
Part bodyPart = multipart.getBodyPart(i);
|
||||
for (Part bodyPart : multipart.getBodyParts()) {
|
||||
viewables.addAll(getViewables(bodyPart, attachments));
|
||||
}
|
||||
}
|
||||
@ -1547,9 +1518,7 @@ public class MimeUtility {
|
||||
throws MessagingException {
|
||||
List<Viewable> viewables = new ArrayList<Viewable>();
|
||||
|
||||
int childCount = multipart.getCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
Part part = multipart.getBodyPart(i);
|
||||
for (Part part : multipart.getBodyParts()) {
|
||||
Body body = part.getBody();
|
||||
if (body instanceof Multipart) {
|
||||
Multipart innerMultipart = (Multipart) body;
|
||||
@ -1612,9 +1581,7 @@ public class MimeUtility {
|
||||
List<Viewable> viewables = new ArrayList<Viewable>();
|
||||
|
||||
boolean partFound = false;
|
||||
int childCount = multipart.getCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
Part part = multipart.getBodyPart(i);
|
||||
for (Part part : multipart.getBodyParts()) {
|
||||
Body body = part.getBody();
|
||||
if (body instanceof Multipart) {
|
||||
Multipart innerMultipart = (Multipart) body;
|
||||
@ -1698,9 +1665,7 @@ public class MimeUtility {
|
||||
*/
|
||||
private static void findAttachments(Multipart multipart, Set<Part> knownTextParts,
|
||||
List<Part> attachments) {
|
||||
int childCount = multipart.getCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
Part part = multipart.getBodyPart(i);
|
||||
for (Part part : multipart.getBodyParts()) {
|
||||
Body body = part.getBody();
|
||||
if (body instanceof Multipart) {
|
||||
Multipart innerMultipart = (Multipart) body;
|
||||
@ -2049,7 +2014,7 @@ public class MimeUtility {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Boolean isPartTextualBody(Part part) throws MessagingException {
|
||||
private static Boolean isPartTextualBody(Part part) throws MessagingException {
|
||||
String disposition = part.getDisposition();
|
||||
String dispositionType = null;
|
||||
String dispositionFilename = null;
|
||||
@ -2137,7 +2102,7 @@ public class MimeUtility {
|
||||
*
|
||||
* @see #MIME_TYPE_REPLACEMENT_MAP
|
||||
*/
|
||||
public static String canonicalizeMimeType(String mimeType) {
|
||||
private static String canonicalizeMimeType(String mimeType) {
|
||||
String lowerCaseMimeType = mimeType.toLowerCase(Locale.US);
|
||||
for (String[] mimeTypeMapEntry : MIME_TYPE_REPLACEMENT_MAP) {
|
||||
if (mimeTypeMapEntry[0].equals(lowerCaseMimeType)) {
|
||||
@ -3422,8 +3387,7 @@ public class MimeUtility {
|
||||
} else if (part.isMimeType("multipart/alternative") &&
|
||||
firstBody instanceof MimeMultipart) {
|
||||
MimeMultipart multipart = (MimeMultipart) firstBody;
|
||||
for (int i = 0, count = multipart.getCount(); i < count; i++) {
|
||||
BodyPart bodyPart = multipart.getBodyPart(i);
|
||||
for (BodyPart bodyPart : multipart.getBodyParts()) {
|
||||
String bodyText = getTextFromPart(bodyPart);
|
||||
if (bodyText != null) {
|
||||
if (text.isEmpty() && bodyPart.isMimeType("text/plain")) {
|
||||
|
@ -16,7 +16,7 @@ public class TextBody implements Body {
|
||||
*/
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
||||
|
||||
private String mBody;
|
||||
private final String mBody;
|
||||
private String mEncoding;
|
||||
private String mCharset = "UTF-8";
|
||||
// Length of the message composed (as opposed to quoted). I don't like the name of this variable and am open to
|
||||
@ -29,6 +29,7 @@ public class TextBody implements Body {
|
||||
this.mBody = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||
if (mBody != null) {
|
||||
byte[] bytes = mBody.getBytes(mCharset);
|
||||
@ -54,6 +55,7 @@ public class TextBody implements Body {
|
||||
/**
|
||||
* Returns an InputStream that reads this body's text.
|
||||
*/
|
||||
@Override
|
||||
public InputStream getInputStream() throws MessagingException {
|
||||
try {
|
||||
byte[] b;
|
||||
@ -68,6 +70,7 @@ public class TextBody implements Body {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncoding(String encoding) {
|
||||
mEncoding = encoding;
|
||||
}
|
||||
|
@ -204,7 +204,10 @@ public class TextBodyBuilder {
|
||||
return mQuotedTextHtml;
|
||||
}
|
||||
|
||||
public String textToHtmlFragment(String text) {
|
||||
/**
|
||||
* protected for unit-test purposes
|
||||
*/
|
||||
protected String textToHtmlFragment(String text) {
|
||||
return HtmlConverter.textToHtmlFragment(text);
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
@ -16,8 +15,6 @@ import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
@ -29,8 +26,11 @@ import java.security.Security;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Deque;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@ -64,6 +64,7 @@ import com.fsck.k9.K9;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.controller.MessageRetrievalListener;
|
||||
import com.fsck.k9.helper.StringUtils;
|
||||
import com.fsck.k9.helper.UrlEncodingHelper;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.helper.power.TracingPowerManager;
|
||||
import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
|
||||
@ -116,7 +117,7 @@ public class ImapStore extends Store {
|
||||
|
||||
private static int FETCH_WINDOW_SIZE = 100;
|
||||
|
||||
private Set<Flag> mPermanentFlagsIndex = new HashSet<Flag>();
|
||||
private Set<Flag> mPermanentFlagsIndex = EnumSet.noneOf(Flag.class);
|
||||
|
||||
private static final String CAPABILITY_IDLE = "IDLE";
|
||||
private static final String CAPABILITY_AUTH_CRAM_MD5 = "AUTH=CRAM-MD5";
|
||||
@ -133,8 +134,6 @@ public class ImapStore extends Store {
|
||||
private static final String CAPABILITY_COMPRESS_DEFLATE = "COMPRESS=DEFLATE";
|
||||
private static final String COMMAND_COMPRESS_DEFLATE = "COMPRESS DEFLATE";
|
||||
|
||||
private static final Message[] EMPTY_MESSAGE_ARRAY = new Message[0];
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
/**
|
||||
@ -198,31 +197,26 @@ public class ImapStore extends Store {
|
||||
}
|
||||
|
||||
if (imapUri.getUserInfo() != null) {
|
||||
try {
|
||||
String userinfo = imapUri.getUserInfo();
|
||||
String[] userInfoParts = userinfo.split(":");
|
||||
String userinfo = imapUri.getUserInfo();
|
||||
String[] userInfoParts = userinfo.split(":");
|
||||
|
||||
if (userinfo.endsWith(":")) {
|
||||
// Password is empty. This can only happen after an account was imported.
|
||||
authenticationType = AuthType.valueOf(userInfoParts[0]);
|
||||
username = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
} else if (userInfoParts.length == 2) {
|
||||
authenticationType = AuthType.PLAIN;
|
||||
username = URLDecoder.decode(userInfoParts[0], "UTF-8");
|
||||
password = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
} else if (userInfoParts.length == 3) {
|
||||
authenticationType = AuthType.valueOf(userInfoParts[0]);
|
||||
username = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
if (userinfo.endsWith(":")) {
|
||||
// Password is empty. This can only happen after an account was imported.
|
||||
authenticationType = AuthType.valueOf(userInfoParts[0]);
|
||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[1]);
|
||||
} else if (userInfoParts.length == 2) {
|
||||
authenticationType = AuthType.PLAIN;
|
||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]);
|
||||
password = UrlEncodingHelper.decodeUtf8(userInfoParts[1]);
|
||||
} else if (userInfoParts.length == 3) {
|
||||
authenticationType = AuthType.valueOf(userInfoParts[0]);
|
||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[1]);
|
||||
|
||||
if (AuthType.EXTERNAL == authenticationType) {
|
||||
clientCertificateAlias = URLDecoder.decode(userInfoParts[2], "UTF-8");
|
||||
} else {
|
||||
password = URLDecoder.decode(userInfoParts[2], "UTF-8");
|
||||
}
|
||||
if (AuthType.EXTERNAL == authenticationType) {
|
||||
clientCertificateAlias = UrlEncodingHelper.decodeUtf8(userInfoParts[2]);
|
||||
} else {
|
||||
password = UrlEncodingHelper.decodeUtf8(userInfoParts[2]);
|
||||
}
|
||||
} catch (UnsupportedEncodingException enc) {
|
||||
// This shouldn't happen since the encoding is hardcoded to UTF-8
|
||||
throw new IllegalArgumentException("Couldn't urldecode username or password.", enc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,19 +254,11 @@ public class ImapStore extends Store {
|
||||
* @see ImapStore#decodeUri(String)
|
||||
*/
|
||||
public static String createUri(ServerSettings server) {
|
||||
String userEnc;
|
||||
String passwordEnc;
|
||||
String clientCertificateAliasEnc;
|
||||
try {
|
||||
userEnc = URLEncoder.encode(server.username, "UTF-8");
|
||||
passwordEnc = (server.password != null) ?
|
||||
URLEncoder.encode(server.password, "UTF-8") : "";
|
||||
clientCertificateAliasEnc = (server.clientCertificateAlias != null) ?
|
||||
URLEncoder.encode(server.clientCertificateAlias, "UTF-8") : "";
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalArgumentException("Could not encode username or password", e);
|
||||
}
|
||||
String userEnc = UrlEncodingHelper.encodeUtf8(server.username);
|
||||
String passwordEnc = (server.password != null) ?
|
||||
UrlEncodingHelper.encodeUtf8(server.password) : "";
|
||||
String clientCertificateAliasEnc = (server.clientCertificateAlias != null) ?
|
||||
UrlEncodingHelper.encodeUtf8(server.clientCertificateAlias) : "";
|
||||
|
||||
String scheme;
|
||||
switch (server.connectionSecurity) {
|
||||
@ -439,7 +425,7 @@ public class ImapStore extends Store {
|
||||
|
||||
private static final SimpleDateFormat RFC3501_DATE = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
|
||||
|
||||
private LinkedList<ImapConnection> mConnections =
|
||||
private final Deque<ImapConnection> mConnections =
|
||||
new LinkedList<ImapConnection>();
|
||||
|
||||
/**
|
||||
@ -453,7 +439,7 @@ public class ImapStore extends Store {
|
||||
* requests. This cache lets us make sure we always reuse, if possible, for a given
|
||||
* folder name.
|
||||
*/
|
||||
private HashMap<String, ImapFolder> mFolderCache = new HashMap<String, ImapFolder>();
|
||||
private final Map<String, ImapFolder> mFolderCache = new HashMap<String, ImapFolder>();
|
||||
|
||||
public ImapStore(Account account) throws MessagingException {
|
||||
super(account);
|
||||
@ -756,18 +742,10 @@ public class ImapStore extends Store {
|
||||
}
|
||||
|
||||
private String encodeFolderName(String name) {
|
||||
try {
|
||||
ByteBuffer bb = mModifiedUtf7Charset.encode(name);
|
||||
byte[] b = new byte[bb.limit()];
|
||||
bb.get(b);
|
||||
return new String(b, "US-ASCII");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
/*
|
||||
* The only thing that can throw this is getBytes("US-ASCII") and if US-ASCII doesn't
|
||||
* exist we're totally screwed.
|
||||
*/
|
||||
throw new RuntimeException("Unable to encode folder name: " + name, uee);
|
||||
}
|
||||
ByteBuffer bb = mModifiedUtf7Charset.encode(name);
|
||||
byte[] b = new byte[bb.limit()];
|
||||
bb.get(b);
|
||||
return new String(b, Charset.forName("US-ASCII"));
|
||||
}
|
||||
|
||||
private String decodeFolderName(String name) throws CharacterCodingException {
|
||||
@ -775,18 +753,11 @@ public class ImapStore extends Store {
|
||||
* Convert the encoded name to US-ASCII, then pass it through the modified UTF-7
|
||||
* decoder and return the Unicode String.
|
||||
*/
|
||||
try {
|
||||
// Make sure the decoder throws an exception if it encounters an invalid encoding.
|
||||
CharsetDecoder decoder = mModifiedUtf7Charset.newDecoder().onMalformedInput(CodingErrorAction.REPORT);
|
||||
CharBuffer cb = decoder.decode(ByteBuffer.wrap(name.getBytes("US-ASCII")));
|
||||
return cb.toString();
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
/*
|
||||
* The only thing that can throw this is getBytes("US-ASCII") and if US-ASCII doesn't
|
||||
* exist we're totally screwed.
|
||||
*/
|
||||
throw new RuntimeException("Unable to decode folder name: " + name, uee);
|
||||
}
|
||||
// Make sure the decoder throws an exception if it encounters an invalid encoding.
|
||||
CharsetDecoder decoder = mModifiedUtf7Charset.newDecoder().onMalformedInput(CodingErrorAction.REPORT);
|
||||
CharBuffer cb = decoder.decode(ByteBuffer.wrap(name.getBytes(Charset.forName("US-ASCII"))));
|
||||
return cb.toString();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1121,22 +1092,22 @@ public class ImapStore extends Store {
|
||||
* @return The mapping of original message UIDs to the new server UIDs.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> copyMessages(Message[] messages, Folder folder)
|
||||
public Map<String, String> copyMessages(List<? extends Message> messages, Folder folder)
|
||||
throws MessagingException {
|
||||
if (!(folder instanceof ImapFolder)) {
|
||||
throw new MessagingException("ImapFolder.copyMessages passed non-ImapFolder");
|
||||
}
|
||||
|
||||
if (messages.length == 0) {
|
||||
if (messages.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ImapFolder iFolder = (ImapFolder)folder;
|
||||
checkOpen(); //only need READ access
|
||||
|
||||
String[] uids = new String[messages.length];
|
||||
for (int i = 0, count = messages.length; i < count; i++) {
|
||||
uids[i] = messages[i].getUid();
|
||||
String[] uids = new String[messages.size()];
|
||||
for (int i = 0, count = messages.size(); i < count; i++) {
|
||||
uids[i] = messages.get(i).getUid();
|
||||
}
|
||||
|
||||
try {
|
||||
@ -1222,21 +1193,21 @@ public class ImapStore extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||
if (messages.length == 0)
|
||||
public Map<String, String> moveMessages(List<? extends Message> messages, Folder folder) throws MessagingException {
|
||||
if (messages.isEmpty())
|
||||
return null;
|
||||
Map<String, String> uidMap = copyMessages(messages, folder);
|
||||
setFlags(messages, new Flag[] { Flag.DELETED }, true);
|
||||
setFlags(messages, Collections.singleton(Flag.DELETED), true);
|
||||
return uidMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Message[] messages, String trashFolderName) throws MessagingException {
|
||||
if (messages.length == 0)
|
||||
public void delete(List<? extends Message> messages, String trashFolderName) throws MessagingException {
|
||||
if (messages.isEmpty())
|
||||
return;
|
||||
|
||||
if (trashFolderName == null || getName().equalsIgnoreCase(trashFolderName)) {
|
||||
setFlags(messages, new Flag[] { Flag.DELETED }, true);
|
||||
setFlags(messages, Collections.singleton(Flag.DELETED), true);
|
||||
} else {
|
||||
ImapFolder remoteTrashFolder = (ImapFolder)getStore().getFolder(trashFolderName);
|
||||
String remoteTrashName = encodeString(encodeFolderName(remoteTrashFolder.getPrefixedName()));
|
||||
@ -1252,7 +1223,7 @@ public class ImapStore extends Store {
|
||||
|
||||
if (exists(remoteTrashName)) {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "IMAPMessage.delete: copying remote " + messages.length + " messages to '" + trashFolderName + "' for " + getLogId());
|
||||
Log.d(K9.LOG_TAG, "IMAPMessage.delete: copying remote " + messages.size() + " messages to '" + trashFolderName + "' for " + getLogId());
|
||||
|
||||
moveMessages(messages, remoteTrashFolder);
|
||||
} else {
|
||||
@ -1302,13 +1273,14 @@ public class ImapStore extends Store {
|
||||
protected long getHighestUid() {
|
||||
try {
|
||||
ImapSearcher searcher = new ImapSearcher() {
|
||||
@Override
|
||||
public List<ImapResponse> search() throws IOException, MessagingException {
|
||||
return executeSimpleCommand("UID SEARCH *:*");
|
||||
}
|
||||
};
|
||||
Message[] messages = search(searcher, null).toArray(EMPTY_MESSAGE_ARRAY);
|
||||
if (messages.length > 0) {
|
||||
return Long.parseLong(messages[0].getUid());
|
||||
List<? extends Message> messages = search(searcher, null);
|
||||
if (messages.size() > 0) {
|
||||
return Long.parseLong(messages.get(0).getUid());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Unable to find highest UID in folder " + getName(), e);
|
||||
@ -1329,12 +1301,12 @@ public class ImapStore extends Store {
|
||||
|
||||
|
||||
@Override
|
||||
public Message[] getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener)
|
||||
public List<? extends Message> getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
return getMessages(start, end, earliestDate, false, listener);
|
||||
}
|
||||
|
||||
protected Message[] getMessages(final int start, final int end, Date earliestDate, final boolean includeDeleted, final MessageRetrievalListener listener)
|
||||
protected List<? extends Message> getMessages(final int start, final int end, Date earliestDate, final boolean includeDeleted, final MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
if (start < 1 || end < 1 || end < start) {
|
||||
throw new MessagingException(
|
||||
@ -1351,39 +1323,42 @@ public class ImapStore extends Store {
|
||||
|
||||
|
||||
ImapSearcher searcher = new ImapSearcher() {
|
||||
@Override
|
||||
public List<ImapResponse> search() throws IOException, MessagingException {
|
||||
return executeSimpleCommand(String.format(Locale.US, "UID SEARCH %d:%d%s%s", start, end, dateSearchString, includeDeleted ? "" : " NOT DELETED"));
|
||||
}
|
||||
};
|
||||
return search(searcher, listener).toArray(EMPTY_MESSAGE_ARRAY);
|
||||
return search(searcher, listener);
|
||||
|
||||
}
|
||||
protected Message[] getMessages(final List<Long> mesgSeqs, final boolean includeDeleted, final MessageRetrievalListener listener)
|
||||
protected List<? extends Message> getMessages(final List<Long> mesgSeqs, final boolean includeDeleted, final MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
ImapSearcher searcher = new ImapSearcher() {
|
||||
@Override
|
||||
public List<ImapResponse> search() throws IOException, MessagingException {
|
||||
return executeSimpleCommand(String.format("UID SEARCH %s%s", Utility.combine(mesgSeqs.toArray(), ','), includeDeleted ? "" : " NOT DELETED"));
|
||||
}
|
||||
};
|
||||
return search(searcher, listener).toArray(EMPTY_MESSAGE_ARRAY);
|
||||
return search(searcher, listener);
|
||||
}
|
||||
|
||||
protected Message[] getMessagesFromUids(final List<String> mesgUids, final boolean includeDeleted, final MessageRetrievalListener listener)
|
||||
protected List<? extends Message> getMessagesFromUids(final List<String> mesgUids, final boolean includeDeleted, final MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
ImapSearcher searcher = new ImapSearcher() {
|
||||
@Override
|
||||
public List<ImapResponse> search() throws IOException, MessagingException {
|
||||
return executeSimpleCommand(String.format("UID SEARCH UID %s%s", Utility.combine(mesgUids.toArray(), ','), includeDeleted ? "" : " NOT DELETED"));
|
||||
}
|
||||
};
|
||||
return search(searcher, listener).toArray(EMPTY_MESSAGE_ARRAY);
|
||||
return search(searcher, listener);
|
||||
}
|
||||
|
||||
private List<Message> search(ImapSearcher searcher, MessageRetrievalListener listener) throws MessagingException {
|
||||
|
||||
checkOpen(); //only need READ access
|
||||
ArrayList<Message> messages = new ArrayList<Message>();
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
try {
|
||||
ArrayList<Long> uids = new ArrayList<Long>();
|
||||
List<Long> uids = new ArrayList<Long>();
|
||||
List<ImapResponse> responses = searcher.search(); //
|
||||
for (ImapResponse response : responses) {
|
||||
if (response.mTag == null) {
|
||||
@ -1420,19 +1395,19 @@ public class ImapStore extends Store {
|
||||
|
||||
|
||||
@Override
|
||||
public Message[] getMessages(MessageRetrievalListener listener) throws MessagingException {
|
||||
public List<? extends Message> getMessages(MessageRetrievalListener listener) throws MessagingException {
|
||||
return getMessages(null, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message[] getMessages(String[] uids, MessageRetrievalListener listener)
|
||||
public List<? extends Message> getMessages(String[] uids, MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
checkOpen(); //only need READ access
|
||||
ArrayList<Message> messages = new ArrayList<Message>();
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
try {
|
||||
if (uids == null) {
|
||||
List<ImapResponse> responses = executeSimpleCommand("UID SEARCH 1:* NOT DELETED");
|
||||
ArrayList<String> tempUids = new ArrayList<String>();
|
||||
List<String> tempUids = new ArrayList<String>();
|
||||
for (ImapResponse response : responses) {
|
||||
if (ImapResponseParser.equalsIgnoreCase(response.get(0), "SEARCH")) {
|
||||
for (int i = 1, count = response.size(); i < count; i++) {
|
||||
@ -1455,17 +1430,17 @@ public class ImapStore extends Store {
|
||||
} catch (IOException ioe) {
|
||||
throw ioExceptionHandler(mConnection, ioe);
|
||||
}
|
||||
return messages.toArray(EMPTY_MESSAGE_ARRAY);
|
||||
return messages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(Message[] messages, FetchProfile fp, MessageRetrievalListener listener)
|
||||
public void fetch(List<? extends Message> messages, FetchProfile fp, MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
if (messages == null || messages.length == 0) {
|
||||
if (messages == null || messages.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
checkOpen(); //only need READ access
|
||||
List<String> uids = new ArrayList<String>(messages.length);
|
||||
List<String> uids = new ArrayList<String>(messages.size());
|
||||
HashMap<String, Message> messageMap = new HashMap<String, Message>();
|
||||
for (Message msg : messages) {
|
||||
String uid = msg.getUid();
|
||||
@ -1507,8 +1482,8 @@ public class ImapStore extends Store {
|
||||
|
||||
|
||||
|
||||
for (int windowStart = 0; windowStart < messages.length; windowStart += (FETCH_WINDOW_SIZE)) {
|
||||
List<String> uidWindow = uids.subList(windowStart, Math.min((windowStart + FETCH_WINDOW_SIZE), messages.length));
|
||||
for (int windowStart = 0; windowStart < messages.size(); windowStart += (FETCH_WINDOW_SIZE)) {
|
||||
List<String> uidWindow = uids.subList(windowStart, Math.min((windowStart + FETCH_WINDOW_SIZE), messages.size()));
|
||||
|
||||
try {
|
||||
mConnection.sendCommand(String.format("UID FETCH %s (%s)",
|
||||
@ -1834,7 +1809,7 @@ public class ImapStore extends Store {
|
||||
* For each part in the message we're going to add a new BodyPart and parse
|
||||
* into it.
|
||||
*/
|
||||
ImapBodyPart bp = new ImapBodyPart();
|
||||
MimeBodyPart bp = new MimeBodyPart();
|
||||
if (id.equalsIgnoreCase("TEXT")) {
|
||||
parseBodyStructure(bs.getList(i), bp, Integer.toString(i + 1));
|
||||
} else {
|
||||
@ -1970,10 +1945,6 @@ public class ImapStore extends Store {
|
||||
|
||||
if (part instanceof ImapMessage) {
|
||||
((ImapMessage) part).setSize(size);
|
||||
} else if (part instanceof ImapBodyPart) {
|
||||
((ImapBodyPart) part).setSize(size);
|
||||
} else {
|
||||
throw new MessagingException("Unknown part type " + part.toString());
|
||||
}
|
||||
part.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA, id);
|
||||
}
|
||||
@ -1994,7 +1965,7 @@ public class ImapStore extends Store {
|
||||
* @return The mapping of original message UIDs to the new server UIDs.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> appendMessages(Message[] messages) throws MessagingException {
|
||||
public Map<String, String> appendMessages(List<? extends Message> messages) throws MessagingException {
|
||||
open(OPEN_MODE_RW);
|
||||
checkOpen();
|
||||
try {
|
||||
@ -2066,7 +2037,7 @@ public class ImapStore extends Store {
|
||||
* with the behavior of other similar methods (copyMessages, moveMessages) which
|
||||
* return null.
|
||||
*/
|
||||
return (uidMap.size() == 0) ? null : uidMap;
|
||||
return (uidMap.isEmpty()) ? null : uidMap;
|
||||
} catch (IOException ioe) {
|
||||
throw ioExceptionHandler(mConnection, ioe);
|
||||
}
|
||||
@ -2117,8 +2088,8 @@ public class ImapStore extends Store {
|
||||
}
|
||||
}
|
||||
|
||||
private String combineFlags(Flag[] flags) {
|
||||
ArrayList<String> flagNames = new ArrayList<String>();
|
||||
private String combineFlags(Iterable<Flag> flags) {
|
||||
List<String> flagNames = new ArrayList<String>();
|
||||
for (Flag flag : flags) {
|
||||
if (flag == Flag.SEEN) {
|
||||
flagNames.add("\\Seen");
|
||||
@ -2139,7 +2110,7 @@ public class ImapStore extends Store {
|
||||
|
||||
|
||||
@Override
|
||||
public void setFlags(Flag[] flags, boolean value)
|
||||
public void setFlags(Set<Flag> flags, boolean value)
|
||||
throws MessagingException {
|
||||
open(OPEN_MODE_RW);
|
||||
checkOpen();
|
||||
@ -2174,13 +2145,13 @@ public class ImapStore extends Store {
|
||||
|
||||
|
||||
@Override
|
||||
public void setFlags(Message[] messages, Flag[] flags, boolean value)
|
||||
public void setFlags(List<? extends Message> messages, final Set<Flag> flags, boolean value)
|
||||
throws MessagingException {
|
||||
open(OPEN_MODE_RW);
|
||||
checkOpen();
|
||||
String[] uids = new String[messages.length];
|
||||
for (int i = 0, count = messages.length; i < count; i++) {
|
||||
uids[i] = messages[i].getUid();
|
||||
String[] uids = new String[messages.size()];
|
||||
for (int i = 0, count = messages.size(); i < count; i++) {
|
||||
uids[i] = messages.get(i).getUid();
|
||||
}
|
||||
try {
|
||||
executeSimpleCommand(String.format("UID STORE %s %sFLAGS.SILENT (%s)",
|
||||
@ -2241,7 +2212,7 @@ public class ImapStore extends Store {
|
||||
* @throws MessagingException On any error.
|
||||
*/
|
||||
@Override
|
||||
public List<Message> search(final String queryString, final Flag[] requiredFlags, final Flag[] forbiddenFlags)
|
||||
public List<Message> search(final String queryString, final Set<Flag> requiredFlags, final Set<Flag> forbiddenFlags)
|
||||
throws MessagingException {
|
||||
|
||||
if (!mAccount.allowRemoteSearch()) {
|
||||
@ -2250,6 +2221,7 @@ public class ImapStore extends Store {
|
||||
|
||||
// Setup the searcher
|
||||
final ImapSearcher searcher = new ImapSearcher() {
|
||||
@Override
|
||||
public List<ImapResponse> search() throws IOException, MessagingException {
|
||||
String imapQuery = "UID SEARCH ";
|
||||
if (requiredFlags != null) {
|
||||
@ -2345,12 +2317,12 @@ public class ImapStore extends Store {
|
||||
* A cacheable class that stores the details for a single IMAP connection.
|
||||
*/
|
||||
public static class ImapConnection {
|
||||
protected Socket mSocket;
|
||||
protected PeekableInputStream mIn;
|
||||
protected OutputStream mOut;
|
||||
protected ImapResponseParser mParser;
|
||||
protected int mNextCommandTag;
|
||||
protected Set<String> capabilities = new HashSet<String>();
|
||||
private Socket mSocket;
|
||||
private PeekableInputStream mIn;
|
||||
private OutputStream mOut;
|
||||
private ImapResponseParser mParser;
|
||||
private int mNextCommandTag;
|
||||
private Set<String> capabilities = new HashSet<String>();
|
||||
|
||||
private ImapSettings mSettings;
|
||||
|
||||
@ -2761,10 +2733,10 @@ public class ImapStore extends Store {
|
||||
return response;
|
||||
}
|
||||
|
||||
protected ArrayList<ImapResponse> readStatusResponse(String tag,
|
||||
protected List<ImapResponse> readStatusResponse(String tag,
|
||||
String commandToLog, UntaggedHandler untaggedHandler)
|
||||
throws IOException, MessagingException {
|
||||
ArrayList<ImapResponse> responses = new ArrayList<ImapResponse>();
|
||||
List<ImapResponse> responses = new ArrayList<ImapResponse>();
|
||||
ImapResponse response;
|
||||
do {
|
||||
response = mParser.readResponse();
|
||||
@ -2931,11 +2903,6 @@ public class ImapStore extends Store {
|
||||
this.mSize = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(InputStream in) throws IOException, MessagingException {
|
||||
super.parse(in);
|
||||
}
|
||||
|
||||
public void setFlagInternal(Flag flag, boolean set) throws MessagingException {
|
||||
super.setFlag(flag, set);
|
||||
}
|
||||
@ -2944,28 +2911,18 @@ public class ImapStore extends Store {
|
||||
@Override
|
||||
public void setFlag(Flag flag, boolean set) throws MessagingException {
|
||||
super.setFlag(flag, set);
|
||||
mFolder.setFlags(new Message[] { this }, new Flag[] { flag }, set);
|
||||
mFolder.setFlags(Collections.singletonList(this), Collections.singleton(flag), set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String trashFolderName) throws MessagingException {
|
||||
getFolder().delete(new Message[] { this }, trashFolderName);
|
||||
}
|
||||
}
|
||||
|
||||
static class ImapBodyPart extends MimeBodyPart {
|
||||
public ImapBodyPart() throws MessagingException {
|
||||
super();
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.mSize = size;
|
||||
getFolder().delete(Collections.singletonList(this), trashFolderName);
|
||||
}
|
||||
}
|
||||
|
||||
static class ImapException extends MessagingException {
|
||||
private static final long serialVersionUID = 3725007182205882394L;
|
||||
String mAlertText;
|
||||
private final String mAlertText;
|
||||
|
||||
public ImapException(String message, String alertText) {
|
||||
super(message, true);
|
||||
@ -2976,22 +2933,19 @@ public class ImapStore extends Store {
|
||||
return mAlertText;
|
||||
}
|
||||
|
||||
public void setAlertText(String alertText) {
|
||||
mAlertText = alertText;
|
||||
}
|
||||
}
|
||||
|
||||
public class ImapFolderPusher extends ImapFolder implements UntaggedHandler {
|
||||
final PushReceiver receiver;
|
||||
Thread listeningThread = null;
|
||||
final AtomicBoolean stop = new AtomicBoolean(false);
|
||||
final AtomicBoolean idling = new AtomicBoolean(false);
|
||||
final AtomicBoolean doneSent = new AtomicBoolean(false);
|
||||
final AtomicInteger delayTime = new AtomicInteger(NORMAL_DELAY_TIME);
|
||||
final AtomicInteger idleFailureCount = new AtomicInteger(0);
|
||||
final AtomicBoolean needsPoll = new AtomicBoolean(false);
|
||||
List<ImapResponse> storedUntaggedResponses = new ArrayList<ImapResponse>();
|
||||
TracingWakeLock wakeLock = null;
|
||||
private final PushReceiver receiver;
|
||||
private Thread listeningThread = null;
|
||||
private final AtomicBoolean stop = new AtomicBoolean(false);
|
||||
private final AtomicBoolean idling = new AtomicBoolean(false);
|
||||
private final AtomicBoolean doneSent = new AtomicBoolean(false);
|
||||
private final AtomicInteger delayTime = new AtomicInteger(NORMAL_DELAY_TIME);
|
||||
private final AtomicInteger idleFailureCount = new AtomicInteger(0);
|
||||
private final AtomicBoolean needsPoll = new AtomicBoolean(false);
|
||||
private List<ImapResponse> storedUntaggedResponses = new ArrayList<ImapResponse>();
|
||||
private TracingWakeLock wakeLock = null;
|
||||
|
||||
public ImapFolderPusher(ImapStore store, String name, PushReceiver nReceiver) {
|
||||
super(store, name);
|
||||
@ -3029,6 +2983,7 @@ public class ImapStore extends Store {
|
||||
|
||||
public void start() {
|
||||
Runnable runner = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
wakeLock.acquire(K9.PUSH_WAKE_LOCK_TIMEOUT);
|
||||
if (K9.DEBUG)
|
||||
@ -3254,9 +3209,9 @@ public class ImapStore extends Store {
|
||||
Log.e(K9.LOG_TAG, "Unable to get oldUidNext for " + getLogId(), e);
|
||||
}
|
||||
|
||||
Message[] messageArray = getMessages(end, end, null, true, null);
|
||||
if (messageArray != null && messageArray.length > 0) {
|
||||
long newUid = Long.parseLong(messageArray[0].getUid());
|
||||
List<? extends Message> messageList = getMessages(end, end, null, true, null);
|
||||
if (messageList != null && messageList.size() > 0) {
|
||||
long newUid = Long.parseLong(messageList.get(0).getUid());
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "Got newUid " + newUid + " for message " + end + " on " + getLogId());
|
||||
long startUid = oldUidNext;
|
||||
@ -3284,12 +3239,10 @@ public class ImapStore extends Store {
|
||||
|
||||
private void syncMessages(List<Long> flagSyncMsgSeqs) {
|
||||
try {
|
||||
Message[] messageArray = null;
|
||||
|
||||
messageArray = getMessages(flagSyncMsgSeqs, true, null);
|
||||
List<? extends Message> messageList = getMessages(flagSyncMsgSeqs, true, null);
|
||||
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
messages.addAll(Arrays.asList(messageArray));
|
||||
messages.addAll(messageList);
|
||||
pushMessages(messages, false);
|
||||
|
||||
} catch (Exception e) {
|
||||
@ -3301,7 +3254,7 @@ public class ImapStore extends Store {
|
||||
List<Message> messages = new ArrayList<Message>(removeUids.size());
|
||||
|
||||
try {
|
||||
Message[] existingMessages = getMessagesFromUids(removeUids, true, null);
|
||||
List<? extends Message> existingMessages = getMessagesFromUids(removeUids, true, null);
|
||||
for (Message existingMessage : existingMessages) {
|
||||
needsPoll.set(true);
|
||||
msgSeqUidMap.clear();
|
||||
@ -3428,6 +3381,7 @@ public class ImapStore extends Store {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAsyncUntaggedResponse(ImapResponse response) {
|
||||
if (K9.DEBUG)
|
||||
Log.v(K9.LOG_TAG, "Got async response: " + response);
|
||||
@ -3478,17 +3432,18 @@ public class ImapStore extends Store {
|
||||
}
|
||||
|
||||
public class ImapPusher implements Pusher {
|
||||
final ImapStore mStore;
|
||||
private final ImapStore mStore;
|
||||
final PushReceiver mReceiver;
|
||||
private long lastRefresh = -1;
|
||||
|
||||
HashMap<String, ImapFolderPusher> folderPushers = new HashMap<String, ImapFolderPusher>();
|
||||
final Map<String, ImapFolderPusher> folderPushers = new HashMap<String, ImapFolderPusher>();
|
||||
|
||||
public ImapPusher(ImapStore store, PushReceiver receiver) {
|
||||
mStore = store;
|
||||
mReceiver = receiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(List<String> folderNames) {
|
||||
stop();
|
||||
synchronized (folderPushers) {
|
||||
@ -3504,6 +3459,7 @@ public class ImapStore extends Store {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
synchronized (folderPushers) {
|
||||
for (ImapFolderPusher folderPusher : folderPushers.values()) {
|
||||
@ -3516,6 +3472,7 @@ public class ImapStore extends Store {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "Requested stop of IMAP pusher");
|
||||
@ -3534,14 +3491,17 @@ public class ImapStore extends Store {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRefreshInterval() {
|
||||
return (getAccount().getIdleRefreshMinutes() * 60 * 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastRefresh() {
|
||||
return lastRefresh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastRefresh(long lastRefresh) {
|
||||
this.lastRefresh = lastRefresh;
|
||||
}
|
||||
@ -3590,9 +3550,9 @@ public class ImapStore extends Store {
|
||||
}
|
||||
|
||||
private static class FetchBodyCallback implements ImapResponseParser.IImapResponseCallback {
|
||||
private HashMap<String, Message> mMessageMap;
|
||||
private Map<String, Message> mMessageMap;
|
||||
|
||||
FetchBodyCallback(HashMap<String, Message> mesageMap) {
|
||||
FetchBodyCallback(Map<String, Message> mesageMap) {
|
||||
mMessageMap = mesageMap;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -99,7 +99,7 @@ public class LockableDatabase {
|
||||
}
|
||||
|
||||
try {
|
||||
openOrCreateDataspace(mApplication);
|
||||
openOrCreateDataspace();
|
||||
} catch (UnavailableStorageException e) {
|
||||
Log.e(K9.LOG_TAG, "Unable to open DB on mount", e);
|
||||
}
|
||||
@ -346,7 +346,7 @@ public class LockableDatabase {
|
||||
mStorageProviderId = newProviderId;
|
||||
|
||||
// re-initialize this class with the new Uri
|
||||
openOrCreateDataspace(mApplication);
|
||||
openOrCreateDataspace();
|
||||
} finally {
|
||||
unlockWrite(newProviderId);
|
||||
}
|
||||
@ -358,7 +358,7 @@ public class LockableDatabase {
|
||||
public void open() throws UnavailableStorageException {
|
||||
lockWrite();
|
||||
try {
|
||||
openOrCreateDataspace(mApplication);
|
||||
openOrCreateDataspace();
|
||||
} finally {
|
||||
unlockWrite();
|
||||
}
|
||||
@ -367,33 +367,20 @@ public class LockableDatabase {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param application
|
||||
* @throws UnavailableStorageException
|
||||
*/
|
||||
protected void openOrCreateDataspace(final Application application) throws UnavailableStorageException {
|
||||
private void openOrCreateDataspace() throws UnavailableStorageException {
|
||||
|
||||
lockWrite();
|
||||
try {
|
||||
final File databaseFile = prepareStorage(mStorageProviderId);
|
||||
try {
|
||||
if (StorageManager.InternalStorageProvider.ID.equals(mStorageProviderId)) {
|
||||
// internal storage
|
||||
mDb = application.openOrCreateDatabase(databaseFile.getName(), Context.MODE_PRIVATE, null);
|
||||
} else {
|
||||
// external storage
|
||||
mDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, null);
|
||||
}
|
||||
doOpenOrCreateDb(databaseFile);
|
||||
} catch (SQLiteException e) {
|
||||
// try to gracefully handle DB corruption - see issue 2537
|
||||
Log.w(K9.LOG_TAG, "Unable to open DB " + databaseFile + " - removing file and retrying", e);
|
||||
databaseFile.delete();
|
||||
if (StorageManager.InternalStorageProvider.ID.equals(mStorageProviderId)) {
|
||||
// internal storage
|
||||
mDb = application.openOrCreateDatabase(databaseFile.getName(), Context.MODE_PRIVATE, null);
|
||||
} else {
|
||||
// external storage
|
||||
mDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, null);
|
||||
}
|
||||
doOpenOrCreateDb(databaseFile);
|
||||
}
|
||||
if (mDb.getVersion() != mSchemaDefinition.getVersion()) {
|
||||
mSchemaDefinition.doDbUpgrade(mDb);
|
||||
@ -403,6 +390,17 @@ public class LockableDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
private void doOpenOrCreateDb(final File databaseFile) {
|
||||
if (StorageManager.InternalStorageProvider.ID.equals(mStorageProviderId)) {
|
||||
// internal storage
|
||||
mDb = mApplication.openOrCreateDatabase(databaseFile.getName(), Context.MODE_PRIVATE,
|
||||
null);
|
||||
} else {
|
||||
// external storage
|
||||
mDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param providerId
|
||||
* Never <code>null</code>.
|
||||
@ -412,10 +410,8 @@ public class LockableDatabase {
|
||||
protected File prepareStorage(final String providerId) throws UnavailableStorageException {
|
||||
final StorageManager storageManager = getStorageManager();
|
||||
|
||||
final File databaseFile;
|
||||
final File databaseParentDir;
|
||||
databaseFile = storageManager.getDatabase(uUid, providerId);
|
||||
databaseParentDir = databaseFile.getParentFile();
|
||||
final File databaseFile = storageManager.getDatabase(uUid, providerId);
|
||||
final File databaseParentDir = databaseFile.getParentFile();
|
||||
if (databaseParentDir.isFile()) {
|
||||
// should be safe to unconditionally delete clashing file: user is not supposed to mess with our directory
|
||||
databaseParentDir.delete();
|
||||
@ -428,11 +424,8 @@ public class LockableDatabase {
|
||||
Utility.touchFile(databaseParentDir, ".nomedia");
|
||||
}
|
||||
|
||||
final File attachmentDir;
|
||||
final File attachmentParentDir;
|
||||
attachmentDir = storageManager
|
||||
.getAttachmentDirectory(uUid, providerId);
|
||||
attachmentParentDir = attachmentDir.getParentFile();
|
||||
final File attachmentDir = storageManager.getAttachmentDirectory(uUid, providerId);
|
||||
final File attachmentParentDir = attachmentDir.getParentFile();
|
||||
if (!attachmentParentDir.exists()) {
|
||||
attachmentParentDir.mkdirs();
|
||||
Utility.touchFile(attachmentParentDir, ".nomedia");
|
||||
@ -467,7 +460,8 @@ public class LockableDatabase {
|
||||
try {
|
||||
mDb.close();
|
||||
} catch (Exception e) {
|
||||
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "Exception caught in DB close: " + e.getMessage());
|
||||
}
|
||||
final StorageManager storageManager = getStorageManager();
|
||||
try {
|
||||
@ -482,6 +476,8 @@ public class LockableDatabase {
|
||||
attachmentDirectory.delete();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (K9.DEBUG)
|
||||
Log.d(K9.LOG_TAG, "Exception caught in clearing attachments: " + e.getMessage());
|
||||
}
|
||||
try {
|
||||
deleteDatabase(storageManager.getDatabase(uUid, mStorageProviderId));
|
||||
@ -490,7 +486,7 @@ public class LockableDatabase {
|
||||
}
|
||||
|
||||
if (recreate) {
|
||||
openOrCreateDataspace(mApplication);
|
||||
openOrCreateDataspace();
|
||||
} else {
|
||||
// stop waiting for mount/unmount events
|
||||
getStorageManager().removeListener(mStorageListener);
|
||||
|
@ -7,6 +7,7 @@ import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.controller.MessageRetrievalListener;
|
||||
import com.fsck.k9.helper.UrlEncodingHelper;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.*;
|
||||
import com.fsck.k9.mail.filter.Base64;
|
||||
@ -23,6 +24,8 @@ import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.HashMap;
|
||||
@ -115,28 +118,23 @@ public class Pop3Store extends Store {
|
||||
|
||||
AuthType authType = AuthType.PLAIN;
|
||||
if (pop3Uri.getUserInfo() != null) {
|
||||
try {
|
||||
int userIndex = 0, passwordIndex = 1;
|
||||
String userinfo = pop3Uri.getUserInfo();
|
||||
String[] userInfoParts = userinfo.split(":");
|
||||
if (userInfoParts.length > 2 || userinfo.endsWith(":") ) {
|
||||
// If 'userinfo' ends with ":" the password is empty. This can only happen
|
||||
// after an account was imported (so authType and username are present).
|
||||
userIndex++;
|
||||
passwordIndex++;
|
||||
authType = AuthType.valueOf(userInfoParts[0]);
|
||||
int userIndex = 0, passwordIndex = 1;
|
||||
String userinfo = pop3Uri.getUserInfo();
|
||||
String[] userInfoParts = userinfo.split(":");
|
||||
if (userInfoParts.length > 2 || userinfo.endsWith(":") ) {
|
||||
// If 'userinfo' ends with ":" the password is empty. This can only happen
|
||||
// after an account was imported (so authType and username are present).
|
||||
userIndex++;
|
||||
passwordIndex++;
|
||||
authType = AuthType.valueOf(userInfoParts[0]);
|
||||
}
|
||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[userIndex]);
|
||||
if (userInfoParts.length > passwordIndex) {
|
||||
if (authType == AuthType.EXTERNAL) {
|
||||
clientCertificateAlias = UrlEncodingHelper.decodeUtf8(userInfoParts[passwordIndex]);
|
||||
} else {
|
||||
password = UrlEncodingHelper.decodeUtf8(userInfoParts[passwordIndex]);
|
||||
}
|
||||
username = URLDecoder.decode(userInfoParts[userIndex], "UTF-8");
|
||||
if (userInfoParts.length > passwordIndex) {
|
||||
if (authType == AuthType.EXTERNAL) {
|
||||
clientCertificateAlias = URLDecoder.decode(userInfoParts[passwordIndex], "UTF-8");
|
||||
} else {
|
||||
password = URLDecoder.decode(userInfoParts[passwordIndex], "UTF-8");
|
||||
}
|
||||
}
|
||||
} catch (UnsupportedEncodingException enc) {
|
||||
// This shouldn't happen since the encoding is hardcoded to UTF-8
|
||||
throw new IllegalArgumentException("Couldn't urldecode username or password.", enc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,19 +154,11 @@ public class Pop3Store extends Store {
|
||||
* @see Pop3Store#decodeUri(String)
|
||||
*/
|
||||
public static String createUri(ServerSettings server) {
|
||||
String userEnc;
|
||||
String passwordEnc;
|
||||
String clientCertificateAliasEnc;
|
||||
try {
|
||||
userEnc = URLEncoder.encode(server.username, "UTF-8");
|
||||
passwordEnc = (server.password != null) ?
|
||||
URLEncoder.encode(server.password, "UTF-8") : "";
|
||||
clientCertificateAliasEnc = (server.clientCertificateAlias != null) ?
|
||||
URLEncoder.encode(server.clientCertificateAlias, "UTF-8") : "";
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalArgumentException("Could not encode username or password", e);
|
||||
}
|
||||
String userEnc = UrlEncodingHelper.encodeUtf8(server.username);
|
||||
String passwordEnc = (server.password != null) ?
|
||||
UrlEncodingHelper.encodeUtf8(server.password) : "";
|
||||
String clientCertificateAliasEnc = (server.clientCertificateAlias != null) ?
|
||||
UrlEncodingHelper.encodeUtf8(server.clientCertificateAlias) : "";
|
||||
|
||||
String scheme;
|
||||
switch (server.connectionSecurity) {
|
||||
@ -208,7 +198,7 @@ public class Pop3Store extends Store {
|
||||
private String mClientCertificateAlias;
|
||||
private AuthType mAuthType;
|
||||
private ConnectionSecurity mConnectionSecurity;
|
||||
private HashMap<String, Folder> mFolders = new HashMap<String, Folder>();
|
||||
private Map<String, Folder> mFolders = new HashMap<String, Folder>();
|
||||
private Pop3Capabilities mCapabilities;
|
||||
|
||||
/**
|
||||
@ -286,9 +276,9 @@ public class Pop3Store extends Store {
|
||||
private Socket mSocket;
|
||||
private InputStream mIn;
|
||||
private OutputStream mOut;
|
||||
private HashMap<String, Pop3Message> mUidToMsgMap = new HashMap<String, Pop3Message>();
|
||||
private HashMap<Integer, Pop3Message> mMsgNumToMsgMap = new HashMap<Integer, Pop3Message>();
|
||||
private HashMap<String, Integer> mUidToMsgNumMap = new HashMap<String, Integer>();
|
||||
private Map<String, Pop3Message> mUidToMsgMap = new HashMap<String, Pop3Message>();
|
||||
private Map<Integer, Pop3Message> mMsgNumToMsgMap = new HashMap<Integer, Pop3Message>();
|
||||
private Map<String, Integer> mUidToMsgNumMap = new HashMap<String, Integer>();
|
||||
private String mName;
|
||||
private int mMessageCount;
|
||||
|
||||
@ -578,7 +568,7 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message[] getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener)
|
||||
public List<? extends Message> getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
if (start < 1 || end < 1 || end < start) {
|
||||
throw new MessagingException(String.format(Locale.US, "Invalid message set %d %d",
|
||||
@ -589,7 +579,7 @@ public class Pop3Store extends Store {
|
||||
} catch (IOException ioe) {
|
||||
throw new MessagingException("getMessages", ioe);
|
||||
}
|
||||
ArrayList<Message> messages = new ArrayList<Message>();
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
int i = 0;
|
||||
for (int msgNum = start; msgNum <= end; msgNum++) {
|
||||
Pop3Message message = mMsgNumToMsgMap.get(msgNum);
|
||||
@ -611,7 +601,7 @@ public class Pop3Store extends Store {
|
||||
listener.messageFinished(message, i++, (end - start) + 1);
|
||||
}
|
||||
}
|
||||
return messages.toArray(new Message[messages.size()]);
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -698,7 +688,7 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
}
|
||||
|
||||
private void indexUids(ArrayList<String> uids)
|
||||
private void indexUids(List<String> uids)
|
||||
throws MessagingException, IOException {
|
||||
Set<String> unindexedUids = new HashSet<String>();
|
||||
for (String uid : uids) {
|
||||
@ -753,12 +743,12 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message[] getMessages(MessageRetrievalListener listener) throws MessagingException {
|
||||
public List<? extends Message> getMessages(MessageRetrievalListener listener) throws MessagingException {
|
||||
throw new UnsupportedOperationException("Pop3: No getMessages");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message[] getMessages(String[] uids, MessageRetrievalListener listener)
|
||||
public List<? extends Message> getMessages(String[] uids, MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
throw new UnsupportedOperationException("Pop3: No getMessages by uids");
|
||||
}
|
||||
@ -771,12 +761,12 @@ public class Pop3Store extends Store {
|
||||
* @throws MessagingException
|
||||
*/
|
||||
@Override
|
||||
public void fetch(Message[] messages, FetchProfile fp, MessageRetrievalListener listener)
|
||||
public void fetch(List<? extends Message> messages, FetchProfile fp, MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
if (messages == null || messages.length == 0) {
|
||||
if (messages == null || messages.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ArrayList<String> uids = new ArrayList<String>();
|
||||
List<String> uids = new ArrayList<String>();
|
||||
for (Message message : messages) {
|
||||
uids.add(message.getUid());
|
||||
}
|
||||
@ -798,8 +788,8 @@ public class Pop3Store extends Store {
|
||||
} catch (IOException ioe) {
|
||||
throw new MessagingException("fetch", ioe);
|
||||
}
|
||||
for (int i = 0, count = messages.length; i < count; i++) {
|
||||
Message message = messages[i];
|
||||
for (int i = 0, count = messages.size(); i < count; i++) {
|
||||
Message message = messages.get(i);
|
||||
if (!(message instanceof Pop3Message)) {
|
||||
throw new MessagingException("Pop3Store.fetch called with non-Pop3 Message");
|
||||
}
|
||||
@ -837,7 +827,7 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchEnvelope(Message[] messages,
|
||||
private void fetchEnvelope(List<? extends Message> messages,
|
||||
MessageRetrievalListener listener) throws IOException, MessagingException {
|
||||
int unsizedMessages = 0;
|
||||
for (Message message : messages) {
|
||||
@ -853,8 +843,8 @@ public class Pop3Store extends Store {
|
||||
* In extreme cases we'll do a command per message instead of a bulk request
|
||||
* to hopefully save some time and bandwidth.
|
||||
*/
|
||||
for (int i = 0, count = messages.length; i < count; i++) {
|
||||
Message message = messages[i];
|
||||
for (int i = 0, count = messages.size(); i < count; i++) {
|
||||
Message message = messages.get(i);
|
||||
if (!(message instanceof Pop3Message)) {
|
||||
throw new MessagingException("Pop3Store.fetch called with non-Pop3 Message");
|
||||
}
|
||||
@ -877,7 +867,7 @@ public class Pop3Store extends Store {
|
||||
for (Message message : messages) {
|
||||
msgUidIndex.add(message.getUid());
|
||||
}
|
||||
int i = 0, count = messages.length;
|
||||
int i = 0, count = messages.size();
|
||||
String response = executeSimpleCommand(LIST_COMMAND);
|
||||
while ((response = readLine()) != null) {
|
||||
if (response.equals(".")) {
|
||||
@ -968,7 +958,7 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> appendMessages(Message[] messages) throws MessagingException {
|
||||
public Map<String, String> appendMessages(List<? extends Message> messages) throws MessagingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -977,8 +967,8 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Message[] msgs, String trashFolderName) throws MessagingException {
|
||||
setFlags(msgs, new Flag[] { Flag.DELETED }, true);
|
||||
public void delete(List<? extends Message> msgs, String trashFolderName) throws MessagingException {
|
||||
setFlags(msgs, Collections.singleton(Flag.DELETED), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -987,20 +977,20 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlags(Flag[] flags, boolean value) throws MessagingException {
|
||||
throw new UnsupportedOperationException("POP3: No setFlags(Flag[],boolean)");
|
||||
public void setFlags(final Set<Flag> flags, boolean value) throws MessagingException {
|
||||
throw new UnsupportedOperationException("POP3: No setFlags(Set<Flag>,boolean)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlags(Message[] messages, Flag[] flags, boolean value)
|
||||
public void setFlags(List<? extends Message> messages, final Set<Flag> flags, boolean value)
|
||||
throws MessagingException {
|
||||
if (!value || !Utility.arrayContains(flags, Flag.DELETED)) {
|
||||
if (!value || !flags.contains(Flag.DELETED)) {
|
||||
/*
|
||||
* The only flagging we support is setting the Deleted flag.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
ArrayList<String> uids = new ArrayList<String>();
|
||||
List<String> uids = new ArrayList<String>();
|
||||
try {
|
||||
for (Message message : messages) {
|
||||
uids.add(message.getUid());
|
||||
@ -1194,15 +1184,10 @@ public class Pop3Store extends Store {
|
||||
mSize = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parse(InputStream in) throws IOException, MessagingException {
|
||||
super.parse(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlag(Flag flag, boolean set) throws MessagingException {
|
||||
super.setFlag(flag, set);
|
||||
mFolder.setFlags(new Message[] { this }, new Flag[] { flag }, set);
|
||||
mFolder.setFlags(Collections.singletonList(this), Collections.singleton(flag), set);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1240,9 +1225,9 @@ public class Pop3Store extends Store {
|
||||
}
|
||||
|
||||
static class Pop3ResponseInputStream extends InputStream {
|
||||
InputStream mIn;
|
||||
boolean mStartOfLine = true;
|
||||
boolean mFinished;
|
||||
private InputStream mIn;
|
||||
private boolean mStartOfLine = true;
|
||||
private boolean mFinished;
|
||||
|
||||
public Pop3ResponseInputStream(InputStream in) {
|
||||
mIn = in;
|
||||
|
@ -176,12 +176,12 @@ public class StorageManager {
|
||||
/**
|
||||
* The root of the denoted storage. Used for mount points checking.
|
||||
*/
|
||||
protected File mRoot;
|
||||
private File mRoot;
|
||||
|
||||
/**
|
||||
* Choosen base directory
|
||||
*/
|
||||
protected File mApplicationDir;
|
||||
private File mApplicationDir;
|
||||
|
||||
@Override
|
||||
public void init(final Context context) {
|
||||
@ -258,7 +258,7 @@ public class StorageManager {
|
||||
|
||||
public static final String ID = "InternalStorage";
|
||||
|
||||
protected File mRoot;
|
||||
private File mRoot;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
@ -328,13 +328,14 @@ public class StorageManager {
|
||||
/**
|
||||
* Root of the denoted storage.
|
||||
*/
|
||||
protected File mRoot;
|
||||
private File mRoot;
|
||||
|
||||
/**
|
||||
* Choosen base directory.
|
||||
*/
|
||||
protected File mApplicationDirectory;
|
||||
private File mApplicationDirectory;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
@ -392,6 +393,7 @@ public class StorageManager {
|
||||
|
||||
public static final String ID = "HtcIncredibleStorage";
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
@ -428,6 +430,7 @@ public class StorageManager {
|
||||
|
||||
public static final String ID = "SamsungGalaxySStorage";
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import android.util.Log;
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.controller.MessageRetrievalListener;
|
||||
|
||||
import com.fsck.k9.helper.UrlEncodingHelper;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.*;
|
||||
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
||||
@ -40,8 +42,6 @@ import javax.xml.parsers.SAXParserFactory;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.DateFormat;
|
||||
@ -65,8 +65,6 @@ public class WebDavStore extends Store {
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
private static final Message[] EMPTY_MESSAGE_ARRAY = new Message[0];
|
||||
|
||||
// These are the ids used from Exchange server to identify the special folders
|
||||
// http://social.technet.microsoft.com/Forums/en/exchangesvrdevelopment/thread/1cd2e98c-8a12-44bd-a3e3-9c5ee9e4e14d
|
||||
private static final String DAV_MAIL_INBOX_FOLDER = "inbox";
|
||||
@ -139,22 +137,17 @@ public class WebDavStore extends Store {
|
||||
|
||||
String userInfo = webDavUri.getUserInfo();
|
||||
if (userInfo != null) {
|
||||
try {
|
||||
String[] userInfoParts = userInfo.split(":");
|
||||
username = URLDecoder.decode(userInfoParts[0], "UTF-8");
|
||||
String userParts[] = username.split("\\\\", 2);
|
||||
String[] userInfoParts = userInfo.split(":");
|
||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]);
|
||||
String userParts[] = username.split("\\\\", 2);
|
||||
|
||||
if (userParts.length > 1) {
|
||||
alias = userParts[1];
|
||||
} else {
|
||||
alias = username;
|
||||
}
|
||||
if (userInfoParts.length > 1) {
|
||||
password = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
}
|
||||
} catch (UnsupportedEncodingException enc) {
|
||||
// This shouldn't happen since the encoding is hardcoded to UTF-8
|
||||
throw new IllegalArgumentException("Couldn't urldecode username or password.", enc);
|
||||
if (userParts.length > 1) {
|
||||
alias = userParts[1];
|
||||
} else {
|
||||
alias = username;
|
||||
}
|
||||
if (userInfoParts.length > 1) {
|
||||
password = UrlEncodingHelper.decodeUtf8(userInfoParts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,16 +187,9 @@ public class WebDavStore extends Store {
|
||||
* @see WebDavStore#decodeUri(String)
|
||||
*/
|
||||
public static String createUri(ServerSettings server) {
|
||||
String userEnc;
|
||||
String passwordEnc;
|
||||
try {
|
||||
userEnc = URLEncoder.encode(server.username, "UTF-8");
|
||||
passwordEnc = (server.password != null) ?
|
||||
URLEncoder.encode(server.password, "UTF-8") : "";
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalArgumentException("Could not encode username or password", e);
|
||||
}
|
||||
String userEnc = UrlEncodingHelper.encodeUtf8(server.username);
|
||||
String passwordEnc = (server.password != null) ?
|
||||
UrlEncodingHelper.encodeUtf8(server.password) : "";
|
||||
|
||||
String scheme;
|
||||
switch (server.connectionSecurity) {
|
||||
@ -305,7 +291,7 @@ public class WebDavStore extends Store {
|
||||
private String mCachedLoginUrl;
|
||||
|
||||
private Folder mSendFolder = null;
|
||||
private HashMap<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
|
||||
private Map<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
|
||||
|
||||
|
||||
public WebDavStore(Account account) throws MessagingException {
|
||||
@ -375,7 +361,7 @@ public class WebDavStore extends Store {
|
||||
|
||||
@Override
|
||||
public List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException {
|
||||
LinkedList<Folder> folderList = new LinkedList<Folder>();
|
||||
List<Folder> folderList = new LinkedList<Folder>();
|
||||
/**
|
||||
* We have to check authentication here so we have the proper URL stored
|
||||
*/
|
||||
@ -385,13 +371,13 @@ public class WebDavStore extends Store {
|
||||
* Firstly we get the "special" folders list (inbox, outbox, etc)
|
||||
* and setup the account accordingly
|
||||
*/
|
||||
HashMap<String, String> headers = new HashMap<String, String>();
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
DataSet dataset = new DataSet();
|
||||
headers.put("Depth", "0");
|
||||
headers.put("Brief", "t");
|
||||
dataset = processRequest(this.mUrl, "PROPFIND", getSpecialFoldersList(), headers);
|
||||
|
||||
HashMap<String, String> specialFoldersMap = dataset.getSpecialFolderToUrl();
|
||||
Map<String, String> specialFoldersMap = dataset.getSpecialFolderToUrl();
|
||||
String folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_INBOX_FOLDER));
|
||||
if (folderName != null) {
|
||||
mAccount.setAutoExpandFolderName(folderName);
|
||||
@ -479,7 +465,6 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
if (folderSlash > 0) {
|
||||
String folderName;
|
||||
String fullPathName;
|
||||
|
||||
// Removes the final slash if present
|
||||
@ -489,17 +474,8 @@ public class WebDavStore extends Store {
|
||||
fullPathName = folderUrl.substring(folderSlash + 1);
|
||||
|
||||
// Decodes the url-encoded folder name (i.e. "My%20folder" => "My Folder"
|
||||
try {
|
||||
folderName = java.net.URLDecoder.decode(fullPathName, "UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
/**
|
||||
* If we don't support UTF-8 there's a problem, don't decode
|
||||
* it then
|
||||
*/
|
||||
folderName = fullPathName;
|
||||
}
|
||||
|
||||
return folderName;
|
||||
return UrlEncodingHelper.decodeUtf8(fullPathName);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -847,7 +823,7 @@ public class WebDavStore extends Store {
|
||||
request.setMethod("POST");
|
||||
|
||||
// Build the POST data.
|
||||
ArrayList<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>();
|
||||
List<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>();
|
||||
pairs.add(new BasicNameValuePair("destination", mUrl));
|
||||
pairs.add(new BasicNameValuePair("username", mUsername));
|
||||
pairs.add(new BasicNameValuePair("password", mPassword));
|
||||
@ -1059,7 +1035,7 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
private InputStream sendRequest(String url, String method, StringEntity messageBody,
|
||||
HashMap<String, String> headers, boolean tryAuth)
|
||||
Map<String, String> headers, boolean tryAuth)
|
||||
throws MessagingException {
|
||||
InputStream istream = null;
|
||||
|
||||
@ -1137,12 +1113,12 @@ public class WebDavStore extends Store {
|
||||
* not all requests will need them. There are two signatures to support calls that don't require parsing of the
|
||||
* response.
|
||||
*/
|
||||
private DataSet processRequest(String url, String method, String messageBody, HashMap<String, String> headers)
|
||||
private DataSet processRequest(String url, String method, String messageBody, Map<String, String> headers)
|
||||
throws MessagingException {
|
||||
return processRequest(url, method, messageBody, headers, true);
|
||||
}
|
||||
|
||||
private DataSet processRequest(String url, String method, String messageBody, HashMap<String, String> headers,
|
||||
private DataSet processRequest(String url, String method, String messageBody, Map<String, String> headers,
|
||||
boolean needsParsing)
|
||||
throws MessagingException {
|
||||
DataSet dataset = new DataSet();
|
||||
@ -1219,11 +1195,11 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessages(Message[] messages) throws MessagingException {
|
||||
public void sendMessages(List<? extends Message> messages) throws MessagingException {
|
||||
WebDavFolder tmpFolder = (WebDavStore.WebDavFolder) getFolder(mAccount.getDraftsFolderName());
|
||||
try {
|
||||
tmpFolder.open(Folder.OPEN_MODE_RW);
|
||||
Message[] retMessages = tmpFolder.appendWebDavMessages(messages);
|
||||
List<? extends Message> retMessages = tmpFolder.appendWebDavMessages(messages);
|
||||
|
||||
tmpFolder.moveMessages(retMessages, getSendSpoolFolder());
|
||||
} finally {
|
||||
@ -1258,21 +1234,16 @@ public class WebDavStore extends Store {
|
||||
this.mName = name;
|
||||
|
||||
String encodedName = "";
|
||||
try {
|
||||
String[] urlParts = name.split("/");
|
||||
String url = "";
|
||||
for (int i = 0, count = urlParts.length; i < count; i++) {
|
||||
if (i != 0) {
|
||||
url = url + "/" + java.net.URLEncoder.encode(urlParts[i], "UTF-8");
|
||||
} else {
|
||||
url = java.net.URLEncoder.encode(urlParts[i], "UTF-8");
|
||||
}
|
||||
String[] urlParts = name.split("/");
|
||||
String url = "";
|
||||
for (int i = 0, count = urlParts.length; i < count; i++) {
|
||||
if (i != 0) {
|
||||
url = url + "/" + UrlEncodingHelper.encodeUtf8(urlParts[i]);
|
||||
} else {
|
||||
url = UrlEncodingHelper.encodeUtf8(urlParts[i]);
|
||||
}
|
||||
encodedName = url;
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
Log.e(K9.LOG_TAG, "UnsupportedEncodingException URLEncoding folder name, skipping encoded");
|
||||
encodedName = name;
|
||||
}
|
||||
encodedName = url;
|
||||
|
||||
encodedName = encodedName.replaceAll("\\+", "%20");
|
||||
|
||||
@ -1297,38 +1268,38 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> copyMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||
public Map<String, String> copyMessages(List<? extends Message> messages, Folder folder) throws MessagingException {
|
||||
moveOrCopyMessages(messages, folder.getName(), false);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> moveMessages(Message[] messages, Folder folder) throws MessagingException {
|
||||
public Map<String, String> moveMessages(List<? extends Message> messages, Folder folder) throws MessagingException {
|
||||
moveOrCopyMessages(messages, folder.getName(), true);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Message[] msgs, String trashFolderName) throws MessagingException {
|
||||
public void delete(List<? extends Message> msgs, String trashFolderName) throws MessagingException {
|
||||
moveOrCopyMessages(msgs, trashFolderName, true);
|
||||
}
|
||||
|
||||
private void moveOrCopyMessages(Message[] messages, String folderName, boolean isMove)
|
||||
private void moveOrCopyMessages(List<? extends Message> messages, String folderName, boolean isMove)
|
||||
throws MessagingException {
|
||||
String[] uids = new String[messages.length];
|
||||
String[] uids = new String[messages.size()];
|
||||
|
||||
for (int i = 0, count = messages.length; i < count; i++) {
|
||||
uids[i] = messages[i].getUid();
|
||||
for (int i = 0, count = messages.size(); i < count; i++) {
|
||||
uids[i] = messages.get(i).getUid();
|
||||
}
|
||||
String messageBody = "";
|
||||
HashMap<String, String> headers = new HashMap<String, String>();
|
||||
HashMap<String, String> uidToUrl = getMessageUrls(uids);
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
Map<String, String> uidToUrl = getMessageUrls(uids);
|
||||
String[] urls = new String[uids.length];
|
||||
|
||||
for (int i = 0, count = uids.length; i < count; i++) {
|
||||
urls[i] = uidToUrl.get(uids[i]);
|
||||
if (urls[i] == null && messages[i] instanceof WebDavMessage) {
|
||||
WebDavMessage wdMessage = (WebDavMessage) messages[i];
|
||||
if (urls[i] == null && messages.get(i) instanceof WebDavMessage) {
|
||||
WebDavMessage wdMessage = (WebDavMessage) messages.get(i);
|
||||
urls[i] = wdMessage.getUrl();
|
||||
}
|
||||
}
|
||||
@ -1339,7 +1310,7 @@ public class WebDavStore extends Store {
|
||||
headers.put("Brief", "t");
|
||||
headers.put("If-Match", "*");
|
||||
String action = (isMove ? "BMOVE" : "BCOPY");
|
||||
Log.i(K9.LOG_TAG, "Moving " + messages.length + " messages to " + destFolder.mFolderUrl);
|
||||
Log.i(K9.LOG_TAG, "Moving " + messages.size() + " messages to " + destFolder.mFolderUrl);
|
||||
|
||||
processRequest(mFolderUrl, action, messageBody, headers, false);
|
||||
}
|
||||
@ -1347,7 +1318,7 @@ public class WebDavStore extends Store {
|
||||
private int getMessageCount(boolean read) throws MessagingException {
|
||||
String isRead;
|
||||
int messageCount = 0;
|
||||
HashMap<String, String> headers = new HashMap<String, String>();
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
String messageBody;
|
||||
|
||||
if (read) {
|
||||
@ -1431,11 +1402,11 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message[] getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener)
|
||||
public List<? extends Message> getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
ArrayList<Message> messages = new ArrayList<Message>();
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
String[] uids;
|
||||
HashMap<String, String> headers = new HashMap<String, String>();
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
int uidsLength = -1;
|
||||
|
||||
String messageBody;
|
||||
@ -1461,7 +1432,7 @@ public class WebDavStore extends Store {
|
||||
DataSet dataset = processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);
|
||||
|
||||
uids = dataset.getUids();
|
||||
HashMap<String, String> uidToUrl = dataset.getUidToUrl();
|
||||
Map<String, String> uidToUrl = dataset.getUidToUrl();
|
||||
uidsLength = uids.length;
|
||||
|
||||
for (int i = 0; i < uidsLength; i++) {
|
||||
@ -1477,22 +1448,22 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
}
|
||||
|
||||
return messages.toArray(EMPTY_MESSAGE_ARRAY);
|
||||
return messages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message[] getMessages(MessageRetrievalListener listener) throws MessagingException {
|
||||
public List<? extends Message> getMessages(MessageRetrievalListener listener) throws MessagingException {
|
||||
return getMessages(null, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message[] getMessages(String[] uids, MessageRetrievalListener listener) throws MessagingException {
|
||||
ArrayList<Message> messageList = new ArrayList<Message>();
|
||||
Message[] messages;
|
||||
public List<? extends Message> getMessages(String[] uids, MessageRetrievalListener listener) throws MessagingException {
|
||||
List<Message> messageList = new ArrayList<Message>();
|
||||
List<? extends Message> messages;
|
||||
|
||||
if (uids == null ||
|
||||
uids.length == 0) {
|
||||
return messageList.toArray(EMPTY_MESSAGE_ARRAY);
|
||||
return messageList;
|
||||
}
|
||||
|
||||
for (int i = 0, count = uids.length; i < count; i++) {
|
||||
@ -1507,13 +1478,13 @@ public class WebDavStore extends Store {
|
||||
listener.messageFinished(message, i, count);
|
||||
}
|
||||
}
|
||||
messages = messageList.toArray(EMPTY_MESSAGE_ARRAY);
|
||||
messages = messageList;
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
private HashMap<String, String> getMessageUrls(String[] uids) throws MessagingException {
|
||||
HashMap<String, String> headers = new HashMap<String, String>();
|
||||
private Map<String, String> getMessageUrls(String[] uids) throws MessagingException {
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
String messageBody;
|
||||
|
||||
/** Retrieve and parse the XML entity for our messages */
|
||||
@ -1521,16 +1492,16 @@ public class WebDavStore extends Store {
|
||||
headers.put("Brief", "t");
|
||||
|
||||
DataSet dataset = processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);
|
||||
HashMap<String, String> uidToUrl = dataset.getUidToUrl();
|
||||
Map<String, String> uidToUrl = dataset.getUidToUrl();
|
||||
|
||||
return uidToUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(Message[] messages, FetchProfile fp, MessageRetrievalListener listener)
|
||||
public void fetch(List<? extends Message> messages, FetchProfile fp, MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
if (messages == null ||
|
||||
messages.length == 0) {
|
||||
messages.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1562,7 +1533,7 @@ public class WebDavStore extends Store {
|
||||
/**
|
||||
* Fetches the full messages or up to lines lines and passes them to the message parser.
|
||||
*/
|
||||
private void fetchMessages(Message[] messages, MessageRetrievalListener listener, int lines)
|
||||
private void fetchMessages(List<? extends Message> messages, MessageRetrievalListener listener, int lines)
|
||||
throws MessagingException {
|
||||
WebDavHttpClient httpclient;
|
||||
httpclient = getHttpClient();
|
||||
@ -1570,15 +1541,15 @@ public class WebDavStore extends Store {
|
||||
/**
|
||||
* We can't hand off to processRequest() since we need the stream to parse.
|
||||
*/
|
||||
for (int i = 0, count = messages.length; i < count; i++) {
|
||||
for (int i = 0, count = messages.size(); i < count; i++) {
|
||||
WebDavMessage wdMessage;
|
||||
int statusCode = 0;
|
||||
|
||||
if (!(messages[i] instanceof WebDavMessage)) {
|
||||
if (!(messages.get(i) instanceof WebDavMessage)) {
|
||||
throw new MessagingException("WebDavStore fetch called with non-WebDavMessage");
|
||||
}
|
||||
|
||||
wdMessage = (WebDavMessage) messages[i];
|
||||
wdMessage = (WebDavMessage) messages.get(i);
|
||||
|
||||
if (listener != null) {
|
||||
listener.messageStarted(wdMessage.getUid(), i, count);
|
||||
@ -1676,36 +1647,36 @@ public class WebDavStore extends Store {
|
||||
* Fetches and sets the message flags for the supplied messages. The idea is to have this be recursive so that
|
||||
* we do a series of medium calls instead of one large massive call or a large number of smaller calls.
|
||||
*/
|
||||
private void fetchFlags(Message[] startMessages, MessageRetrievalListener listener) throws MessagingException {
|
||||
private void fetchFlags(List<? extends Message> startMessages, MessageRetrievalListener listener) throws MessagingException {
|
||||
HashMap<String, String> headers = new HashMap<String, String>();
|
||||
String messageBody = "";
|
||||
Message[] messages = new Message[20];
|
||||
List<Message> messages = new ArrayList<Message>(20);
|
||||
String[] uids;
|
||||
|
||||
if (startMessages == null ||
|
||||
startMessages.length == 0) {
|
||||
startMessages.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (startMessages.length > 20) {
|
||||
Message[] newMessages = new Message[startMessages.length - 20];
|
||||
for (int i = 0, count = startMessages.length; i < count; i++) {
|
||||
if (startMessages.size() > 20) {
|
||||
List<Message> newMessages = new ArrayList<Message>(startMessages.size() - 20);
|
||||
for (int i = 0, count = startMessages.size(); i < count; i++) {
|
||||
if (i < 20) {
|
||||
messages[i] = startMessages[i];
|
||||
messages.set(i, startMessages.get(i));
|
||||
} else {
|
||||
newMessages[i - 20] = startMessages[i];
|
||||
newMessages.set(i - 20, startMessages.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
fetchFlags(newMessages, listener);
|
||||
} else {
|
||||
messages = startMessages;
|
||||
messages.addAll(startMessages);
|
||||
}
|
||||
|
||||
uids = new String[messages.length];
|
||||
uids = new String[messages.size()];
|
||||
|
||||
for (int i = 0, count = messages.length; i < count; i++) {
|
||||
uids[i] = messages[i].getUid();
|
||||
for (int i = 0, count = messages.size(); i < count; i++) {
|
||||
uids[i] = messages.get(i).getUid();
|
||||
}
|
||||
|
||||
messageBody = getMessageFlagsXml(uids);
|
||||
@ -1716,13 +1687,13 @@ public class WebDavStore extends Store {
|
||||
throw new MessagingException("Data Set from request was null");
|
||||
}
|
||||
|
||||
HashMap<String, Boolean> uidToReadStatus = dataset.getUidToRead();
|
||||
Map<String, Boolean> uidToReadStatus = dataset.getUidToRead();
|
||||
|
||||
for (int i = 0, count = messages.length; i < count; i++) {
|
||||
if (!(messages[i] instanceof WebDavMessage)) {
|
||||
for (int i = 0, count = messages.size(); i < count; i++) {
|
||||
if (!(messages.get(i) instanceof WebDavMessage)) {
|
||||
throw new MessagingException("WebDavStore fetch called with non-WebDavMessage");
|
||||
}
|
||||
WebDavMessage wdMessage = (WebDavMessage) messages[i];
|
||||
WebDavMessage wdMessage = (WebDavMessage) messages.get(i);
|
||||
|
||||
if (listener != null) {
|
||||
listener.messageStarted(wdMessage.getUid(), i, count);
|
||||
@ -1745,37 +1716,37 @@ public class WebDavStore extends Store {
|
||||
* that we do a series of medium calls instead of one large massive call or a large number of smaller calls.
|
||||
* Call it a happy balance
|
||||
*/
|
||||
private void fetchEnvelope(Message[] startMessages, MessageRetrievalListener listener)
|
||||
private void fetchEnvelope(List<? extends Message> startMessages, MessageRetrievalListener listener)
|
||||
throws MessagingException {
|
||||
HashMap<String, String> headers = new HashMap<String, String>();
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
String messageBody = "";
|
||||
String[] uids;
|
||||
Message[] messages = new Message[10];
|
||||
List<Message> messages = new ArrayList<Message>(10);
|
||||
|
||||
if (startMessages == null ||
|
||||
startMessages.length == 0) {
|
||||
startMessages.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (startMessages.length > 10) {
|
||||
Message[] newMessages = new Message[startMessages.length - 10];
|
||||
for (int i = 0, count = startMessages.length; i < count; i++) {
|
||||
if (startMessages.size() > 10) {
|
||||
List<Message> newMessages = new ArrayList<Message>(startMessages.size() - 10);
|
||||
for (int i = 0, count = startMessages.size(); i < count; i++) {
|
||||
if (i < 10) {
|
||||
messages[i] = startMessages[i];
|
||||
messages.set(i, startMessages.get(i));
|
||||
} else {
|
||||
newMessages[i - 10] = startMessages[i];
|
||||
newMessages.set(i - 10,startMessages.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
fetchEnvelope(newMessages, listener);
|
||||
} else {
|
||||
messages = startMessages;
|
||||
messages.addAll(startMessages);
|
||||
}
|
||||
|
||||
uids = new String[messages.length];
|
||||
uids = new String[messages.size()];
|
||||
|
||||
for (int i = 0, count = messages.length; i < count; i++) {
|
||||
uids[i] = messages[i].getUid();
|
||||
for (int i = 0, count = messages.size(); i < count; i++) {
|
||||
uids[i] = messages.get(i).getUid();
|
||||
}
|
||||
|
||||
messageBody = getMessageEnvelopeXml(uids);
|
||||
@ -1784,15 +1755,15 @@ public class WebDavStore extends Store {
|
||||
|
||||
Map<String, ParsedMessageEnvelope> envelopes = dataset.getMessageEnvelopes();
|
||||
|
||||
int count = messages.length;
|
||||
for (int i = messages.length - 1; i >= 0; i--) {
|
||||
if (!(messages[i] instanceof WebDavMessage)) {
|
||||
int count = messages.size();
|
||||
for (int i = messages.size() - 1; i >= 0; i--) {
|
||||
if (!(messages.get(i) instanceof WebDavMessage)) {
|
||||
throw new MessagingException("WebDavStore fetch called with non-WebDavMessage");
|
||||
}
|
||||
WebDavMessage wdMessage = (WebDavMessage) messages[i];
|
||||
WebDavMessage wdMessage = (WebDavMessage) messages.get(i);
|
||||
|
||||
if (listener != null) {
|
||||
listener.messageStarted(messages[i].getUid(), i, count);
|
||||
listener.messageStarted(messages.get(i).getUid(), i, count);
|
||||
}
|
||||
|
||||
ParsedMessageEnvelope envelope = envelopes.get(wdMessage.getUid());
|
||||
@ -1804,18 +1775,18 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
listener.messageFinished(messages[i], i, count);
|
||||
listener.messageFinished(messages.get(i), i, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlags(Message[] messages, Flag[] flags, boolean value)
|
||||
public void setFlags(List<? extends Message> messages, final Set<Flag> flags, boolean value)
|
||||
throws MessagingException {
|
||||
String[] uids = new String[messages.length];
|
||||
String[] uids = new String[messages.size()];
|
||||
|
||||
for (int i = 0, count = messages.length; i < count; i++) {
|
||||
uids[i] = messages[i].getUid();
|
||||
for (int i = 0, count = messages.size(); i < count; i++) {
|
||||
uids[i] = messages.get(i).getUid();
|
||||
}
|
||||
|
||||
for (Flag flag : flags) {
|
||||
@ -1829,8 +1800,8 @@ public class WebDavStore extends Store {
|
||||
|
||||
private void markServerMessagesRead(String[] uids, boolean read) throws MessagingException {
|
||||
String messageBody = "";
|
||||
HashMap<String, String> headers = new HashMap<String, String>();
|
||||
HashMap<String, String> uidToUrl = getMessageUrls(uids);
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
Map<String, String> uidToUrl = getMessageUrls(uids);
|
||||
String[] urls = new String[uids.length];
|
||||
|
||||
for (int i = 0, count = uids.length; i < count; i++) {
|
||||
@ -1845,10 +1816,10 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
private void deleteServerMessages(String[] uids) throws MessagingException {
|
||||
HashMap<String, String> uidToUrl = getMessageUrls(uids);
|
||||
Map<String, String> uidToUrl = getMessageUrls(uids);
|
||||
|
||||
for (String uid : uids) {
|
||||
HashMap<String, String> headers = new HashMap<String, String>();
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
String url = uidToUrl.get(uid);
|
||||
String destinationUrl = generateDeleteUrl(url);
|
||||
|
||||
@ -1875,13 +1846,13 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> appendMessages(Message[] messages) throws MessagingException {
|
||||
public Map<String, String> appendMessages(List<? extends Message> messages) throws MessagingException {
|
||||
appendWebDavMessages(messages);
|
||||
return null;
|
||||
}
|
||||
|
||||
public Message[] appendWebDavMessages(Message[] messages) throws MessagingException {
|
||||
Message[] retMessages = new Message[messages.length];
|
||||
public List<? extends Message> appendWebDavMessages(List<? extends Message> messages) throws MessagingException {
|
||||
List<Message> retMessages = new ArrayList<Message>(messages.size());
|
||||
int ind = 0;
|
||||
|
||||
WebDavHttpClient httpclient = getHttpClient();
|
||||
@ -1910,7 +1881,7 @@ public class WebDavStore extends Store {
|
||||
if (!messageURL.endsWith("/")) {
|
||||
messageURL += "/";
|
||||
}
|
||||
messageURL += URLEncoder.encode(message.getUid() + ":" + System.currentTimeMillis() + ".eml", "UTF-8");
|
||||
messageURL += UrlEncodingHelper.encodeUtf8(message.getUid() + ":" + System.currentTimeMillis() + ".eml");
|
||||
|
||||
Log.i(K9.LOG_TAG, "Uploading message as " + messageURL);
|
||||
|
||||
@ -1936,7 +1907,7 @@ public class WebDavStore extends Store {
|
||||
WebDavMessage retMessage = new WebDavMessage(message.getUid(), this);
|
||||
|
||||
retMessage.setUrl(messageURL);
|
||||
retMessages[ind++] = retMessage;
|
||||
retMessages.set(ind++, retMessage);
|
||||
} catch (Exception e) {
|
||||
throw new MessagingException("Unable to append", e);
|
||||
}
|
||||
@ -1958,9 +1929,9 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlags(Flag[] flags, boolean value) throws MessagingException {
|
||||
public void setFlags(final Set<Flag> flags, boolean value) throws MessagingException {
|
||||
Log.e(K9.LOG_TAG,
|
||||
"Unimplemented method setFlags(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
|
||||
}
|
||||
}
|
||||
@ -1997,12 +1968,9 @@ public class WebDavStore extends Store {
|
||||
* We have to decode, then encode the URL because Exchange likes to not properly encode all characters
|
||||
*/
|
||||
try {
|
||||
end = java.net.URLDecoder.decode(end, "UTF-8");
|
||||
end = java.net.URLEncoder.encode(end, "UTF-8");
|
||||
end = UrlEncodingHelper.decodeUtf8(end);
|
||||
end = UrlEncodingHelper.encodeUtf8(end);
|
||||
end = end.replaceAll("\\+", "%20");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
Log.e(K9.LOG_TAG, "UnsupportedEncodingException caught in setUrl: " + uee + "\nTrace: "
|
||||
+ processException(uee));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
Log.e(K9.LOG_TAG, "IllegalArgumentException caught in setUrl: " + iae + "\nTrace: "
|
||||
+ processException(iae));
|
||||
@ -2029,18 +1997,13 @@ public class WebDavStore extends Store {
|
||||
this.mSize = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(InputStream in) throws IOException, MessagingException {
|
||||
super.parse(in);
|
||||
}
|
||||
|
||||
public void setFlagInternal(Flag flag, boolean set) throws MessagingException {
|
||||
super.setFlag(flag, set);
|
||||
}
|
||||
|
||||
public void setNewHeaders(ParsedMessageEnvelope envelope) throws MessagingException {
|
||||
String[] headers = envelope.getHeaderList();
|
||||
HashMap<String, String> messageHeaders = envelope.getMessageHeaders();
|
||||
Map<String, String> messageHeaders = envelope.getMessageHeaders();
|
||||
|
||||
for (String header : headers) {
|
||||
String headerValue = messageHeaders.get(header);
|
||||
@ -2060,13 +2023,13 @@ public class WebDavStore extends Store {
|
||||
public void delete(String trashFolderName) throws MessagingException {
|
||||
WebDavFolder wdFolder = (WebDavFolder) getFolder();
|
||||
Log.i(K9.LOG_TAG, "Deleting message by moving to " + trashFolderName);
|
||||
wdFolder.moveMessages(new Message[] { this }, wdFolder.getStore().getFolder(trashFolderName));
|
||||
wdFolder.moveMessages(Collections.singletonList(this), wdFolder.getStore().getFolder(trashFolderName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlag(Flag flag, boolean set) throws MessagingException {
|
||||
super.setFlag(flag, set);
|
||||
mFolder.setFlags(new Message[] { this }, new Flag[] { flag }, set);
|
||||
mFolder.setFlags(Collections.singletonList(this), Collections.singleton(flag), set);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2142,8 +2105,8 @@ public class WebDavStore extends Store {
|
||||
|
||||
private boolean mReadStatus = false;
|
||||
private String mUid = "";
|
||||
private HashMap<String, String> mMessageHeaders = new HashMap<String, String>();
|
||||
private ArrayList<String> mHeaders = new ArrayList<String>();
|
||||
private Map<String, String> mMessageHeaders = new HashMap<String, String>();
|
||||
private List<String> mHeaders = new ArrayList<String>();
|
||||
|
||||
public void addHeader(String field, String value) {
|
||||
String headerName = HEADER_MAPPINGS.get(field);
|
||||
@ -2154,7 +2117,7 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
}
|
||||
|
||||
public HashMap<String, String> getMessageHeaders() {
|
||||
public Map<String, String> getMessageHeaders() {
|
||||
return this.mMessageHeaders;
|
||||
}
|
||||
|
||||
@ -2186,9 +2149,9 @@ public class WebDavStore extends Store {
|
||||
* depending on the accessor calls made.
|
||||
*/
|
||||
public class DataSet {
|
||||
private HashMap<String, HashMap<String, String>> mData = new HashMap<String, HashMap<String, String>>();
|
||||
private Map<String, Map<String, String>> mData = new HashMap<String, Map<String, String>>();
|
||||
private StringBuilder mUid = new StringBuilder();
|
||||
private HashMap<String, String> mTempData = new HashMap<String, String>();
|
||||
private Map<String, String> mTempData = new HashMap<String, String>();
|
||||
|
||||
public void addValue(String value, String tagName) {
|
||||
if (tagName.equals("uid")) {
|
||||
@ -2220,9 +2183,9 @@ public class WebDavStore extends Store {
|
||||
/**
|
||||
* Returns a hashmap of special folder name => special folder url
|
||||
*/
|
||||
public HashMap<String, String> getSpecialFolderToUrl() {
|
||||
public Map<String, String> getSpecialFolderToUrl() {
|
||||
// We return the first (and only) map
|
||||
for (HashMap<String, String> folderMap : mData.values()) {
|
||||
for (Map<String, String> folderMap : mData.values()) {
|
||||
return folderMap;
|
||||
}
|
||||
return new HashMap<String, String>();
|
||||
@ -2231,11 +2194,11 @@ public class WebDavStore extends Store {
|
||||
/**
|
||||
* Returns a hashmap of Message UID => Message Url
|
||||
*/
|
||||
public HashMap<String, String> getUidToUrl() {
|
||||
HashMap<String, String> uidToUrl = new HashMap<String, String>();
|
||||
public Map<String, String> getUidToUrl() {
|
||||
Map<String, String> uidToUrl = new HashMap<String, String>();
|
||||
|
||||
for (String uid : mData.keySet()) {
|
||||
HashMap<String, String> data = mData.get(uid);
|
||||
Map<String, String> data = mData.get(uid);
|
||||
String value = data.get("href");
|
||||
if (value != null &&
|
||||
!value.equals("")) {
|
||||
@ -2249,11 +2212,11 @@ public class WebDavStore extends Store {
|
||||
/**
|
||||
* Returns a hashmap of Message UID => Read Status
|
||||
*/
|
||||
public HashMap<String, Boolean> getUidToRead() {
|
||||
HashMap<String, Boolean> uidToRead = new HashMap<String, Boolean>();
|
||||
public Map<String, Boolean> getUidToRead() {
|
||||
Map<String, Boolean> uidToRead = new HashMap<String, Boolean>();
|
||||
|
||||
for (String uid : mData.keySet()) {
|
||||
HashMap<String, String> data = mData.get(uid);
|
||||
Map<String, String> data = mData.get(uid);
|
||||
String readStatus = data.get("read");
|
||||
if (readStatus != null && !readStatus.equals("")) {
|
||||
Boolean value = !readStatus.equals("0");
|
||||
@ -2273,10 +2236,10 @@ public class WebDavStore extends Store {
|
||||
* Returns an array of all hrefs (urls) that were received
|
||||
*/
|
||||
public String[] getHrefs() {
|
||||
ArrayList<String> hrefs = new ArrayList<String>();
|
||||
List<String> hrefs = new ArrayList<String>();
|
||||
|
||||
for (String uid : mData.keySet()) {
|
||||
HashMap<String, String> data = mData.get(uid);
|
||||
Map<String, String> data = mData.get(uid);
|
||||
String href = data.get("href");
|
||||
hrefs.add(href);
|
||||
}
|
||||
@ -2288,7 +2251,7 @@ public class WebDavStore extends Store {
|
||||
* Return an array of all Message UIDs that were received
|
||||
*/
|
||||
public String[] getUids() {
|
||||
ArrayList<String> uids = new ArrayList<String>();
|
||||
List<String> uids = new ArrayList<String>();
|
||||
|
||||
for (String uid : mData.keySet()) {
|
||||
uids.add(uid);
|
||||
@ -2309,7 +2272,7 @@ public class WebDavStore extends Store {
|
||||
int messageCount = 0;
|
||||
|
||||
for (String uid : mData.keySet()) {
|
||||
HashMap<String, String> data = mData.get(uid);
|
||||
Map<String, String> data = mData.get(uid);
|
||||
String count = data.get("visiblecount");
|
||||
|
||||
if (count != null &&
|
||||
@ -2323,14 +2286,14 @@ public class WebDavStore extends Store {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a HashMap of message UID => ParsedMessageEnvelope
|
||||
* Returns a Map of message UID => ParsedMessageEnvelope
|
||||
*/
|
||||
public HashMap<String, ParsedMessageEnvelope> getMessageEnvelopes() {
|
||||
HashMap<String, ParsedMessageEnvelope> envelopes = new HashMap<String, ParsedMessageEnvelope>();
|
||||
public Map<String, ParsedMessageEnvelope> getMessageEnvelopes() {
|
||||
Map<String, ParsedMessageEnvelope> envelopes = new HashMap<String, ParsedMessageEnvelope>();
|
||||
|
||||
for (String uid : mData.keySet()) {
|
||||
ParsedMessageEnvelope envelope = new ParsedMessageEnvelope();
|
||||
HashMap<String, String> data = mData.get(uid);
|
||||
Map<String, String> data = mData.get(uid);
|
||||
|
||||
if (data != null) {
|
||||
for (Map.Entry<String, String> entry : data.entrySet()) {
|
||||
@ -2410,13 +2373,10 @@ public class WebDavStore extends Store {
|
||||
*/
|
||||
try {
|
||||
if (length > 3) {
|
||||
end = java.net.URLDecoder.decode(end, "UTF-8");
|
||||
end = java.net.URLEncoder.encode(end, "UTF-8");
|
||||
end = UrlEncodingHelper.decodeUtf8(end);
|
||||
end = UrlEncodingHelper.encodeUtf8(end);
|
||||
end = end.replaceAll("\\+", "%20");
|
||||
}
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
Log.e(K9.LOG_TAG, "UnsupportedEncodingException caught in HttpGeneric(String uri): " + uee
|
||||
+ "\nTrace: " + processException(uee));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
Log.e(K9.LOG_TAG, "IllegalArgumentException caught in HttpGeneric(String uri): " + iae + "\nTrace: "
|
||||
+ processException(iae));
|
||||
|
@ -48,7 +48,7 @@ public class ImapUtility {
|
||||
* list is returned.
|
||||
*/
|
||||
public static List<String> getImapSequenceValues(String set) {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
List<String> list = new ArrayList<String>();
|
||||
if (set != null) {
|
||||
String[] setItems = set.split(",");
|
||||
for (String item : setItems) {
|
||||
@ -83,7 +83,7 @@ public class ImapUtility {
|
||||
* is returned.
|
||||
*/
|
||||
public static List<String> getImapRangeValues(String range) {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
List<String> list = new ArrayList<String>();
|
||||
try {
|
||||
if (range != null) {
|
||||
int colonPos = range.indexOf(':');
|
||||
|
@ -0,0 +1,34 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.james.mime4j.util.MimeUtil;
|
||||
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.internet.MimeMessage;
|
||||
|
||||
public class AttachmentMessageBodyUtil {
|
||||
public static void writeTo(BinaryAttachmentBody body, OutputStream out) throws IOException,
|
||||
MessagingException {
|
||||
InputStream in = body.getInputStream();
|
||||
try {
|
||||
if (MimeUtil.ENC_7BIT.equalsIgnoreCase(body.getEncoding())) {
|
||||
/*
|
||||
* If we knew the message was already 7bit clean, then it
|
||||
* could be sent along without processing. But since we
|
||||
* don't know, we recursively parse it.
|
||||
*/
|
||||
MimeMessage message = new MimeMessage(in, true);
|
||||
message.setUsing7bitTransport();
|
||||
message.writeTo(out);
|
||||
} else {
|
||||
IOUtils.copy(in, out);
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}
|
58
src/com/fsck/k9/mail/store/local/BinaryAttachmentBody.java
Normal file
58
src/com/fsck/k9/mail/store/local/BinaryAttachmentBody.java
Normal file
@ -0,0 +1,58 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
|
||||
import org.apache.james.mime4j.util.MimeUtil;
|
||||
|
||||
import com.fsck.k9.mail.Body;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.filter.Base64OutputStream;
|
||||
|
||||
/**
|
||||
* Superclass for attachments that contain binary data.
|
||||
* The source for the data differs for the subclasses.
|
||||
*/
|
||||
public abstract class BinaryAttachmentBody implements Body {
|
||||
protected String mEncoding;
|
||||
|
||||
@Override
|
||||
public abstract InputStream getInputStream() throws MessagingException;
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||
InputStream in = getInputStream();
|
||||
try {
|
||||
boolean closeStream = false;
|
||||
if (MimeUtil.isBase64Encoding(mEncoding)) {
|
||||
out = new Base64OutputStream(out);
|
||||
closeStream = true;
|
||||
} else if (MimeUtil.isQuotedPrintableEncoded(mEncoding)){
|
||||
out = new QuotedPrintableOutputStream(out, false);
|
||||
closeStream = true;
|
||||
}
|
||||
|
||||
try {
|
||||
IOUtils.copy(in, out);
|
||||
} finally {
|
||||
if (closeStream) {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncoding(String encoding) throws MessagingException {
|
||||
mEncoding = encoding;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return mEncoding;
|
||||
}
|
||||
}
|
40
src/com/fsck/k9/mail/store/local/LocalAttachmentBody.java
Normal file
40
src/com/fsck/k9/mail/store/local/LocalAttachmentBody.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import android.app.Application;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
|
||||
/**
|
||||
* An attachment whose contents are loaded from an URI.
|
||||
*/
|
||||
public class LocalAttachmentBody extends BinaryAttachmentBody {
|
||||
private Application mApplication;
|
||||
private Uri mUri;
|
||||
|
||||
public LocalAttachmentBody(Uri uri, Application application) {
|
||||
mApplication = application;
|
||||
mUri = uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws MessagingException {
|
||||
try {
|
||||
return mApplication.getContentResolver().openInputStream(mUri);
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
/*
|
||||
* Since it's completely normal for us to try to serve up attachments that
|
||||
* have been blown away, we just return an empty stream.
|
||||
*/
|
||||
return new ByteArrayInputStream(LocalStore.EMPTY_BYTE_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
public Uri getContentUri() {
|
||||
return mUri;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
|
||||
import com.fsck.k9.mail.Body;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.internet.MimeBodyPart;
|
||||
|
||||
public class LocalAttachmentBodyPart extends MimeBodyPart {
|
||||
private long mAttachmentId = -1;
|
||||
|
||||
public LocalAttachmentBodyPart(Body body, long attachmentId) throws MessagingException {
|
||||
super(body);
|
||||
mAttachmentId = attachmentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local attachment id of this body, or -1 if it is not stored.
|
||||
* @return
|
||||
*/
|
||||
public long getAttachmentId() {
|
||||
return mAttachmentId;
|
||||
}
|
||||
|
||||
public void setAttachmentId(long attachmentId) {
|
||||
mAttachmentId = attachmentId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "" + mAttachmentId;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.james.mime4j.util.MimeUtil;
|
||||
|
||||
import android.app.Application;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.fsck.k9.mail.CompositeBody;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
|
||||
/**
|
||||
* A {@link LocalAttachmentBody} extension containing a message/rfc822 type body
|
||||
*
|
||||
*/
|
||||
public class LocalAttachmentMessageBody extends LocalAttachmentBody implements CompositeBody {
|
||||
|
||||
public LocalAttachmentMessageBody(Uri uri, Application application) {
|
||||
super(uri, application);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||
AttachmentMessageBodyUtil.writeTo(this, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsing7bitTransport() throws MessagingException {
|
||||
/*
|
||||
* There's nothing to recurse into here, so there's nothing to do.
|
||||
* The enclosing BodyPart already called setEncoding(MimeUtil.ENC_7BIT). Once
|
||||
* writeTo() is called, the file with the rfc822 body will be opened
|
||||
* for reading and will then be recursed.
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncoding(String encoding) throws MessagingException {
|
||||
if (!MimeUtil.ENC_7BIT.equalsIgnoreCase(encoding)
|
||||
&& !MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) {
|
||||
throw new MessagingException(
|
||||
"Incompatible content-transfer-encoding applied to a CompositeBody");
|
||||
}
|
||||
mEncoding = encoding;
|
||||
}
|
||||
}
|
2199
src/com/fsck/k9/mail/store/local/LocalFolder.java
Normal file
2199
src/com/fsck/k9/mail/store/local/LocalFolder.java
Normal file
File diff suppressed because it is too large
Load Diff
560
src/com/fsck/k9/mail/store/local/LocalMessage.java
Normal file
560
src/com/fsck/k9/mail/store/local/LocalMessage.java
Normal file
@ -0,0 +1,560 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.Folder;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Part;
|
||||
import com.fsck.k9.mail.internet.MimeMessage;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
||||
import com.fsck.k9.mail.store.LockableDatabase.DbCallback;
|
||||
import com.fsck.k9.mail.store.LockableDatabase.WrappedException;
|
||||
|
||||
public class LocalMessage extends MimeMessage {
|
||||
|
||||
private final LocalStore localStore;
|
||||
|
||||
long mId;
|
||||
private int mAttachmentCount;
|
||||
private String mSubject;
|
||||
|
||||
private String mPreview = "";
|
||||
|
||||
private boolean mHeadersLoaded = false;
|
||||
private boolean mMessageDirty = false;
|
||||
|
||||
private long mThreadId;
|
||||
private long mRootId;
|
||||
|
||||
private LocalMessage(LocalStore localStore) {
|
||||
this.localStore = localStore;
|
||||
}
|
||||
|
||||
LocalMessage(LocalStore localStore, String uid, Folder folder) {
|
||||
this.localStore = localStore;
|
||||
this.mUid = uid;
|
||||
this.mFolder = folder;
|
||||
}
|
||||
|
||||
void populateFromGetMessageCursor(Cursor cursor)
|
||||
throws MessagingException {
|
||||
final String subject = cursor.getString(0);
|
||||
this.setSubject(subject == null ? "" : subject);
|
||||
|
||||
Address[] from = Address.unpack(cursor.getString(1));
|
||||
if (from.length > 0) {
|
||||
this.setFrom(from[0]);
|
||||
}
|
||||
this.setInternalSentDate(new Date(cursor.getLong(2)));
|
||||
this.setUid(cursor.getString(3));
|
||||
String flagList = cursor.getString(4);
|
||||
if (flagList != null && flagList.length() > 0) {
|
||||
String[] flags = flagList.split(",");
|
||||
|
||||
for (String flag : flags) {
|
||||
try {
|
||||
this.setFlagInternal(Flag.valueOf(flag), true);
|
||||
}
|
||||
|
||||
catch (Exception e) {
|
||||
if (!"X_BAD_FLAG".equals(flag)) {
|
||||
Log.w(K9.LOG_TAG, "Unable to parse flag " + flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.mId = cursor.getLong(5);
|
||||
this.setRecipients(RecipientType.TO, Address.unpack(cursor.getString(6)));
|
||||
this.setRecipients(RecipientType.CC, Address.unpack(cursor.getString(7)));
|
||||
this.setRecipients(RecipientType.BCC, Address.unpack(cursor.getString(8)));
|
||||
this.setReplyTo(Address.unpack(cursor.getString(9)));
|
||||
|
||||
this.mAttachmentCount = cursor.getInt(10);
|
||||
this.setInternalDate(new Date(cursor.getLong(11)));
|
||||
this.setMessageId(cursor.getString(12));
|
||||
|
||||
final String preview = cursor.getString(14);
|
||||
mPreview = (preview == null ? "" : preview);
|
||||
|
||||
if (this.mFolder == null) {
|
||||
LocalFolder f = new LocalFolder(this.localStore, cursor.getInt(13));
|
||||
f.open(LocalFolder.OPEN_MODE_RW);
|
||||
this.mFolder = f;
|
||||
}
|
||||
|
||||
mThreadId = (cursor.isNull(15)) ? -1 : cursor.getLong(15);
|
||||
mRootId = (cursor.isNull(16)) ? -1 : cursor.getLong(16);
|
||||
|
||||
boolean deleted = (cursor.getInt(17) == 1);
|
||||
boolean read = (cursor.getInt(18) == 1);
|
||||
boolean flagged = (cursor.getInt(19) == 1);
|
||||
boolean answered = (cursor.getInt(20) == 1);
|
||||
boolean forwarded = (cursor.getInt(21) == 1);
|
||||
|
||||
setFlagInternal(Flag.DELETED, deleted);
|
||||
setFlagInternal(Flag.SEEN, read);
|
||||
setFlagInternal(Flag.FLAGGED, flagged);
|
||||
setFlagInternal(Flag.ANSWERED, answered);
|
||||
setFlagInternal(Flag.FORWARDED, forwarded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the message text for display. This always returns an HTML-ified version of the
|
||||
* message, even if it was originally a text-only message.
|
||||
* @return HTML version of message for display purposes or null.
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public String getTextForDisplay() throws MessagingException {
|
||||
String text = null; // First try and fetch an HTML part.
|
||||
Part part = MimeUtility.findFirstPartByMimeType(this, "text/html");
|
||||
if (part == null) {
|
||||
// If that fails, try and get a text part.
|
||||
part = MimeUtility.findFirstPartByMimeType(this, "text/plain");
|
||||
if (part != null && part.getBody() instanceof LocalTextBody) {
|
||||
text = ((LocalTextBody) part.getBody()).getBodyForDisplay();
|
||||
}
|
||||
} else {
|
||||
// We successfully found an HTML part; do the necessary character set decoding.
|
||||
text = MimeUtility.getTextFromPart(part);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
/* Custom version of writeTo that updates the MIME message based on localMessage
|
||||
* changes.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||
if (mMessageDirty) buildMimeRepresentation();
|
||||
super.writeTo(out);
|
||||
}
|
||||
|
||||
void buildMimeRepresentation() throws MessagingException {
|
||||
if (!mMessageDirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.setSubject(mSubject);
|
||||
if (this.mFrom != null && this.mFrom.length > 0) {
|
||||
super.setFrom(this.mFrom[0]);
|
||||
}
|
||||
|
||||
super.setReplyTo(mReplyTo);
|
||||
super.setSentDate(this.getSentDate());
|
||||
super.setRecipients(RecipientType.TO, mTo);
|
||||
super.setRecipients(RecipientType.CC, mCc);
|
||||
super.setRecipients(RecipientType.BCC, mBcc);
|
||||
if (mMessageId != null) super.setMessageId(mMessageId);
|
||||
|
||||
mMessageDirty = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreview() {
|
||||
return mPreview;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSubject() {
|
||||
return mSubject;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setSubject(String subject) throws MessagingException {
|
||||
mSubject = subject;
|
||||
mMessageDirty = true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setMessageId(String messageId) {
|
||||
mMessageId = messageId;
|
||||
mMessageDirty = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAttachments() {
|
||||
return (mAttachmentCount > 0);
|
||||
}
|
||||
|
||||
public int getAttachmentCount() {
|
||||
return mAttachmentCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrom(Address from) throws MessagingException {
|
||||
this.mFrom = new Address[] { from };
|
||||
mMessageDirty = true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setReplyTo(Address[] replyTo) throws MessagingException {
|
||||
if (replyTo == null || replyTo.length == 0) {
|
||||
mReplyTo = null;
|
||||
} else {
|
||||
mReplyTo = replyTo;
|
||||
}
|
||||
mMessageDirty = true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For performance reasons, we add headers instead of setting them (see super implementation)
|
||||
* which removes (expensive) them before adding them
|
||||
*/
|
||||
@Override
|
||||
public void setRecipients(RecipientType type, Address[] addresses) throws MessagingException {
|
||||
if (type == RecipientType.TO) {
|
||||
if (addresses == null || addresses.length == 0) {
|
||||
this.mTo = null;
|
||||
} else {
|
||||
this.mTo = addresses;
|
||||
}
|
||||
} else if (type == RecipientType.CC) {
|
||||
if (addresses == null || addresses.length == 0) {
|
||||
this.mCc = null;
|
||||
} else {
|
||||
this.mCc = addresses;
|
||||
}
|
||||
} else if (type == RecipientType.BCC) {
|
||||
if (addresses == null || addresses.length == 0) {
|
||||
this.mBcc = null;
|
||||
} else {
|
||||
this.mBcc = addresses;
|
||||
}
|
||||
} else {
|
||||
throw new MessagingException("Unrecognized recipient type.");
|
||||
}
|
||||
mMessageDirty = true;
|
||||
}
|
||||
|
||||
public void setFlagInternal(Flag flag, boolean set) throws MessagingException {
|
||||
super.setFlag(flag, set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlag(final Flag flag, final boolean set) throws MessagingException {
|
||||
|
||||
try {
|
||||
this.localStore.database.execute(true, new DbCallback<Void>() {
|
||||
@Override
|
||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
||||
try {
|
||||
if (flag == Flag.DELETED && set) {
|
||||
delete();
|
||||
}
|
||||
|
||||
LocalMessage.super.setFlag(flag, set);
|
||||
} catch (MessagingException e) {
|
||||
throw new WrappedException(e);
|
||||
}
|
||||
/*
|
||||
* Set the flags on the message.
|
||||
*/
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("flags", LocalMessage.this.localStore.serializeFlags(getFlags()));
|
||||
cv.put("read", isSet(Flag.SEEN) ? 1 : 0);
|
||||
cv.put("flagged", isSet(Flag.FLAGGED) ? 1 : 0);
|
||||
cv.put("answered", isSet(Flag.ANSWERED) ? 1 : 0);
|
||||
cv.put("forwarded", isSet(Flag.FORWARDED) ? 1 : 0);
|
||||
|
||||
db.update("messages", cv, "id = ?", new String[] { Long.toString(mId) });
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (WrappedException e) {
|
||||
throw(MessagingException) e.getCause();
|
||||
}
|
||||
|
||||
this.localStore.notifyChange();
|
||||
}
|
||||
|
||||
/*
|
||||
* If a message is being marked as deleted we want to clear out it's content
|
||||
* and attachments as well. Delete will not actually remove the row since we need
|
||||
* to retain the uid for synchronization purposes.
|
||||
*/
|
||||
private void delete() throws MessagingException
|
||||
|
||||
{
|
||||
/*
|
||||
* Delete all of the message's content to save space.
|
||||
*/
|
||||
try {
|
||||
this.localStore.database.execute(true, new DbCallback<Void>() {
|
||||
@Override
|
||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException,
|
||||
UnavailableStorageException {
|
||||
String[] idArg = new String[] { Long.toString(mId) };
|
||||
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("deleted", 1);
|
||||
cv.put("empty", 1);
|
||||
cv.putNull("subject");
|
||||
cv.putNull("sender_list");
|
||||
cv.putNull("date");
|
||||
cv.putNull("to_list");
|
||||
cv.putNull("cc_list");
|
||||
cv.putNull("bcc_list");
|
||||
cv.putNull("preview");
|
||||
cv.putNull("html_content");
|
||||
cv.putNull("text_content");
|
||||
cv.putNull("reply_to_list");
|
||||
|
||||
db.update("messages", cv, "id = ?", idArg);
|
||||
|
||||
/*
|
||||
* Delete all of the message's attachments to save space.
|
||||
* We do this explicit deletion here because we're not deleting the record
|
||||
* in messages, which means our ON DELETE trigger for messages won't cascade
|
||||
*/
|
||||
try {
|
||||
((LocalFolder) mFolder).deleteAttachments(mId);
|
||||
} catch (MessagingException e) {
|
||||
throw new WrappedException(e);
|
||||
}
|
||||
|
||||
db.delete("attachments", "message_id = ?", idArg);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (WrappedException e) {
|
||||
throw(MessagingException) e.getCause();
|
||||
}
|
||||
((LocalFolder)mFolder).deleteHeaders(mId);
|
||||
|
||||
this.localStore.notifyChange();
|
||||
}
|
||||
|
||||
/*
|
||||
* Completely remove a message from the local database
|
||||
*
|
||||
* TODO: document how this updates the thread structure
|
||||
*/
|
||||
@Override
|
||||
public void destroy() throws MessagingException {
|
||||
try {
|
||||
this.localStore.database.execute(true, new DbCallback<Void>() {
|
||||
@Override
|
||||
public Void doDbWork(final SQLiteDatabase db) throws WrappedException,
|
||||
UnavailableStorageException {
|
||||
try {
|
||||
LocalFolder localFolder = (LocalFolder) mFolder;
|
||||
|
||||
localFolder.deleteAttachments(mId);
|
||||
|
||||
if (hasThreadChildren(db, mId)) {
|
||||
// This message has children in the thread structure so we need to
|
||||
// make it an empty message.
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("id", mId);
|
||||
cv.put("folder_id", localFolder.getId());
|
||||
cv.put("deleted", 0);
|
||||
cv.put("message_id", getMessageId());
|
||||
cv.put("empty", 1);
|
||||
|
||||
db.replace("messages", null, cv);
|
||||
|
||||
// Nothing else to do
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the message ID of the parent message if it's empty
|
||||
long currentId = getEmptyThreadParent(db, mId);
|
||||
|
||||
// Delete the placeholder message
|
||||
deleteMessageRow(db, mId);
|
||||
|
||||
/*
|
||||
* Walk the thread tree to delete all empty parents without children
|
||||
*/
|
||||
|
||||
while (currentId != -1) {
|
||||
if (hasThreadChildren(db, currentId)) {
|
||||
// We made sure there are no empty leaf nodes and can stop now.
|
||||
break;
|
||||
}
|
||||
|
||||
// Get ID of the (empty) parent for the next iteration
|
||||
long newId = getEmptyThreadParent(db, currentId);
|
||||
|
||||
// Delete the empty message
|
||||
deleteMessageRow(db, currentId);
|
||||
|
||||
currentId = newId;
|
||||
}
|
||||
|
||||
} catch (MessagingException e) {
|
||||
throw new WrappedException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (WrappedException e) {
|
||||
throw(MessagingException) e.getCause();
|
||||
}
|
||||
|
||||
this.localStore.notifyChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ID of the the given message's parent if the parent is an empty message.
|
||||
*
|
||||
* @param db
|
||||
* {@link SQLiteDatabase} instance to access the database.
|
||||
* @param messageId
|
||||
* The database ID of the message to get the parent for.
|
||||
*
|
||||
* @return Message ID of the parent message if there exists a parent and it is empty.
|
||||
* Otherwise {@code -1}.
|
||||
*/
|
||||
private long getEmptyThreadParent(SQLiteDatabase db, long messageId) {
|
||||
Cursor cursor = db.rawQuery(
|
||||
"SELECT m.id " +
|
||||
"FROM threads t1 " +
|
||||
"JOIN threads t2 ON (t1.parent = t2.id) " +
|
||||
"LEFT JOIN messages m ON (t2.message_id = m.id) " +
|
||||
"WHERE t1.message_id = ? AND m.empty = 1",
|
||||
new String[] { Long.toString(messageId) });
|
||||
|
||||
try {
|
||||
return (cursor.moveToFirst() && !cursor.isNull(0)) ? cursor.getLong(0) : -1;
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether or not a message has child messages in the thread structure.
|
||||
*
|
||||
* @param db
|
||||
* {@link SQLiteDatabase} instance to access the database.
|
||||
* @param messageId
|
||||
* The database ID of the message to get the children for.
|
||||
*
|
||||
* @return {@code true} if the message has children. {@code false} otherwise.
|
||||
*/
|
||||
private boolean hasThreadChildren(SQLiteDatabase db, long messageId) {
|
||||
Cursor cursor = db.rawQuery(
|
||||
"SELECT COUNT(t2.id) " +
|
||||
"FROM threads t1 " +
|
||||
"JOIN threads t2 ON (t2.parent = t1.id) " +
|
||||
"WHERE t1.message_id = ?",
|
||||
new String[] { Long.toString(messageId) });
|
||||
|
||||
try {
|
||||
return (cursor.moveToFirst() && !cursor.isNull(0) && cursor.getLong(0) > 0L);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a message from the 'messages' and 'threads' tables.
|
||||
*
|
||||
* @param db
|
||||
* {@link SQLiteDatabase} instance to access the database.
|
||||
* @param messageId
|
||||
* The database ID of the message to delete.
|
||||
*/
|
||||
private void deleteMessageRow(SQLiteDatabase db, long messageId) {
|
||||
String[] idArg = { Long.toString(messageId) };
|
||||
|
||||
// Delete the message
|
||||
db.delete("messages", "id = ?", idArg);
|
||||
|
||||
// Delete row in 'threads' table
|
||||
// TODO: create trigger for 'messages' table to get rid of the row in 'threads' table
|
||||
db.delete("threads", "message_id = ?", idArg);
|
||||
}
|
||||
|
||||
private void loadHeaders() throws UnavailableStorageException {
|
||||
List<LocalMessage> messages = new ArrayList<LocalMessage>();
|
||||
messages.add(this);
|
||||
mHeadersLoaded = true; // set true before calling populate headers to stop recursion
|
||||
((LocalFolder) mFolder).populateHeaders(messages);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHeader(String name, String value) throws UnavailableStorageException {
|
||||
if (!mHeadersLoaded)
|
||||
loadHeaders();
|
||||
super.addHeader(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeader(String name, String value) throws UnavailableStorageException {
|
||||
if (!mHeadersLoaded)
|
||||
loadHeaders();
|
||||
super.setHeader(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getHeader(String name) throws UnavailableStorageException {
|
||||
if (!mHeadersLoaded)
|
||||
loadHeaders();
|
||||
return super.getHeader(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHeader(String name) throws UnavailableStorageException {
|
||||
if (!mHeadersLoaded)
|
||||
loadHeaders();
|
||||
super.removeHeader(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getHeaderNames() throws UnavailableStorageException {
|
||||
if (!mHeadersLoaded)
|
||||
loadHeaders();
|
||||
return super.getHeaderNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalMessage clone() {
|
||||
LocalMessage message = new LocalMessage(this.localStore);
|
||||
super.copy(message);
|
||||
|
||||
message.mId = mId;
|
||||
message.mAttachmentCount = mAttachmentCount;
|
||||
message.mSubject = mSubject;
|
||||
message.mPreview = mPreview;
|
||||
message.mHeadersLoaded = mHeadersLoaded;
|
||||
message.mMessageDirty = mMessageDirty;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public long getThreadId() {
|
||||
return mThreadId;
|
||||
}
|
||||
|
||||
public long getRootId() {
|
||||
return mRootId;
|
||||
}
|
||||
}
|
1032
src/com/fsck/k9/mail/store/local/LocalStore.java
Normal file
1032
src/com/fsck/k9/mail/store/local/LocalStore.java
Normal file
File diff suppressed because it is too large
Load Diff
20
src/com/fsck/k9/mail/store/local/LocalTextBody.java
Normal file
20
src/com/fsck/k9/mail/store/local/LocalTextBody.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
|
||||
import com.fsck.k9.mail.internet.TextBody;
|
||||
|
||||
public class LocalTextBody extends TextBody {
|
||||
/**
|
||||
* This is an HTML-ified version of the message for display purposes.
|
||||
*/
|
||||
private final String mBodyForDisplay;
|
||||
|
||||
public LocalTextBody(String body, String bodyForDisplay) {
|
||||
super(body);
|
||||
this.mBodyForDisplay = bodyForDisplay;
|
||||
}
|
||||
|
||||
public String getBodyForDisplay() {
|
||||
return mBodyForDisplay;
|
||||
}
|
||||
|
||||
}//LocalTextBody
|
599
src/com/fsck/k9/mail/store/local/StoreSchemaDefinition.java
Normal file
599
src/com/fsck/k9/mail/store/local/StoreSchemaDefinition.java
Normal file
@ -0,0 +1,599 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.Folder;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.store.LockableDatabase;
|
||||
import com.fsck.k9.provider.AttachmentProvider;
|
||||
|
||||
class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private final LocalStore localStore;
|
||||
|
||||
/**
|
||||
* @param localStore
|
||||
*/
|
||||
StoreSchemaDefinition(LocalStore localStore) {
|
||||
this.localStore = localStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return LocalStore.DB_VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDbUpgrade(final SQLiteDatabase db) {
|
||||
try {
|
||||
upgradeDatabase(db);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Exception while upgrading database. Resetting the DB to v0", e);
|
||||
db.setVersion(0);
|
||||
upgradeDatabase(db);
|
||||
}
|
||||
}
|
||||
|
||||
private void upgradeDatabase(final SQLiteDatabase db) {
|
||||
Log.i(K9.LOG_TAG, String.format(Locale.US, "Upgrading database from version %d to version %d",
|
||||
db.getVersion(), LocalStore.DB_VERSION));
|
||||
|
||||
AttachmentProvider.clear(this.localStore.mApplication);
|
||||
|
||||
db.beginTransaction();
|
||||
try {
|
||||
// schema version 29 was when we moved to incremental updates
|
||||
// in the case of a new db or a < v29 db, we blow away and start from scratch
|
||||
if (db.getVersion() < 29) {
|
||||
|
||||
db.execSQL("DROP TABLE IF EXISTS folders");
|
||||
db.execSQL("CREATE TABLE folders (id INTEGER PRIMARY KEY, name TEXT, "
|
||||
+ "last_updated INTEGER, unread_count INTEGER, visible_limit INTEGER, status TEXT, "
|
||||
+ "push_state TEXT, last_pushed INTEGER, flagged_count INTEGER default 0, "
|
||||
+ "integrate INTEGER, top_group INTEGER, poll_class TEXT, push_class TEXT, display_class TEXT, notify_class TEXT"
|
||||
+ ")");
|
||||
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS folder_name ON folders (name)");
|
||||
db.execSQL("DROP TABLE IF EXISTS messages");
|
||||
db.execSQL("CREATE TABLE messages (" +
|
||||
"id INTEGER PRIMARY KEY, " +
|
||||
"deleted INTEGER default 0, " +
|
||||
"folder_id INTEGER, " +
|
||||
"uid TEXT, " +
|
||||
"subject TEXT, " +
|
||||
"date INTEGER, " +
|
||||
"flags TEXT, " +
|
||||
"sender_list TEXT, " +
|
||||
"to_list TEXT, " +
|
||||
"cc_list TEXT, " +
|
||||
"bcc_list TEXT, " +
|
||||
"reply_to_list TEXT, " +
|
||||
"html_content TEXT, " +
|
||||
"text_content TEXT, " +
|
||||
"attachment_count INTEGER, " +
|
||||
"internal_date INTEGER, " +
|
||||
"message_id TEXT, " +
|
||||
"preview TEXT, " +
|
||||
"mime_type TEXT, "+
|
||||
"normalized_subject_hash INTEGER, " +
|
||||
"empty INTEGER, " +
|
||||
"read INTEGER default 0, " +
|
||||
"flagged INTEGER default 0, " +
|
||||
"answered INTEGER default 0, " +
|
||||
"forwarded INTEGER default 0" +
|
||||
")");
|
||||
|
||||
db.execSQL("DROP TABLE IF EXISTS headers");
|
||||
db.execSQL("CREATE TABLE headers (id INTEGER PRIMARY KEY, message_id INTEGER, name TEXT, value TEXT)");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS header_folder ON headers (message_id)");
|
||||
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_uid ON messages (uid, folder_id)");
|
||||
db.execSQL("DROP INDEX IF EXISTS msg_folder_id");
|
||||
db.execSQL("DROP INDEX IF EXISTS msg_folder_id_date");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_folder_id_deleted_date ON messages (folder_id,deleted,internal_date)");
|
||||
|
||||
db.execSQL("DROP INDEX IF EXISTS msg_empty");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_empty ON messages (empty)");
|
||||
|
||||
db.execSQL("DROP INDEX IF EXISTS msg_read");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_read ON messages (read)");
|
||||
|
||||
db.execSQL("DROP INDEX IF EXISTS msg_flagged");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_flagged ON messages (flagged)");
|
||||
|
||||
db.execSQL("DROP INDEX IF EXISTS msg_composite");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_composite ON messages (deleted, empty,folder_id,flagged,read)");
|
||||
|
||||
|
||||
|
||||
db.execSQL("DROP TABLE IF EXISTS threads");
|
||||
db.execSQL("CREATE TABLE threads (" +
|
||||
"id INTEGER PRIMARY KEY, " +
|
||||
"message_id INTEGER, " +
|
||||
"root INTEGER, " +
|
||||
"parent INTEGER" +
|
||||
")");
|
||||
|
||||
db.execSQL("DROP INDEX IF EXISTS threads_message_id");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS threads_message_id ON threads (message_id)");
|
||||
|
||||
db.execSQL("DROP INDEX IF EXISTS threads_root");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS threads_root ON threads (root)");
|
||||
|
||||
db.execSQL("DROP INDEX IF EXISTS threads_parent");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS threads_parent ON threads (parent)");
|
||||
|
||||
db.execSQL("DROP TRIGGER IF EXISTS set_thread_root");
|
||||
db.execSQL("CREATE TRIGGER set_thread_root " +
|
||||
"AFTER INSERT ON threads " +
|
||||
"BEGIN " +
|
||||
"UPDATE threads SET root=id WHERE root IS NULL AND ROWID = NEW.ROWID; " +
|
||||
"END");
|
||||
|
||||
db.execSQL("DROP TABLE IF EXISTS attachments");
|
||||
db.execSQL("CREATE TABLE attachments (id INTEGER PRIMARY KEY, message_id INTEGER,"
|
||||
+ "store_data TEXT, content_uri TEXT, size INTEGER, name TEXT,"
|
||||
+ "mime_type TEXT, content_id TEXT, content_disposition TEXT)");
|
||||
|
||||
db.execSQL("DROP TABLE IF EXISTS pending_commands");
|
||||
db.execSQL("CREATE TABLE pending_commands " +
|
||||
"(id INTEGER PRIMARY KEY, command TEXT, arguments TEXT)");
|
||||
|
||||
db.execSQL("DROP TRIGGER IF EXISTS delete_folder");
|
||||
db.execSQL("CREATE TRIGGER delete_folder BEFORE DELETE ON folders BEGIN DELETE FROM messages WHERE old.id = folder_id; END;");
|
||||
|
||||
db.execSQL("DROP TRIGGER IF EXISTS delete_message");
|
||||
db.execSQL("CREATE TRIGGER delete_message BEFORE DELETE ON messages BEGIN DELETE FROM attachments WHERE old.id = message_id; "
|
||||
+ "DELETE FROM headers where old.id = message_id; END;");
|
||||
} else {
|
||||
// in the case that we're starting out at 29 or newer, run all the needed updates
|
||||
|
||||
if (db.getVersion() < 30) {
|
||||
try {
|
||||
db.execSQL("ALTER TABLE messages ADD deleted INTEGER default 0");
|
||||
} catch (SQLiteException e) {
|
||||
if (! e.toString().startsWith("duplicate column name: deleted")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (db.getVersion() < 31) {
|
||||
db.execSQL("DROP INDEX IF EXISTS msg_folder_id_date");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_folder_id_deleted_date ON messages (folder_id,deleted,internal_date)");
|
||||
}
|
||||
if (db.getVersion() < 32) {
|
||||
db.execSQL("UPDATE messages SET deleted = 1 WHERE flags LIKE '%DELETED%'");
|
||||
}
|
||||
if (db.getVersion() < 33) {
|
||||
|
||||
try {
|
||||
db.execSQL("ALTER TABLE messages ADD preview TEXT");
|
||||
} catch (SQLiteException e) {
|
||||
if (! e.toString().startsWith("duplicate column name: preview")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (db.getVersion() < 34) {
|
||||
try {
|
||||
db.execSQL("ALTER TABLE folders ADD flagged_count INTEGER default 0");
|
||||
} catch (SQLiteException e) {
|
||||
if (! e.getMessage().startsWith("duplicate column name: flagged_count")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (db.getVersion() < 35) {
|
||||
try {
|
||||
db.execSQL("update messages set flags = replace(flags, 'X_NO_SEEN_INFO', 'X_BAD_FLAG')");
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(K9.LOG_TAG, "Unable to get rid of obsolete flag X_NO_SEEN_INFO", e);
|
||||
}
|
||||
}
|
||||
if (db.getVersion() < 36) {
|
||||
try {
|
||||
db.execSQL("ALTER TABLE attachments ADD content_id TEXT");
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(K9.LOG_TAG, "Unable to add content_id column to attachments");
|
||||
}
|
||||
}
|
||||
if (db.getVersion() < 37) {
|
||||
try {
|
||||
db.execSQL("ALTER TABLE attachments ADD content_disposition TEXT");
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(K9.LOG_TAG, "Unable to add content_disposition column to attachments");
|
||||
}
|
||||
}
|
||||
|
||||
// Database version 38 is solely to prune cached attachments now that we clear them better
|
||||
if (db.getVersion() < 39) {
|
||||
try {
|
||||
db.execSQL("DELETE FROM headers WHERE id in (SELECT headers.id FROM headers LEFT JOIN messages ON headers.message_id = messages.id WHERE messages.id IS NULL)");
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(K9.LOG_TAG, "Unable to remove extra header data from the database");
|
||||
}
|
||||
}
|
||||
|
||||
// V40: Store the MIME type for a message.
|
||||
if (db.getVersion() < 40) {
|
||||
try {
|
||||
db.execSQL("ALTER TABLE messages ADD mime_type TEXT");
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(K9.LOG_TAG, "Unable to add mime_type column to messages");
|
||||
}
|
||||
}
|
||||
|
||||
if (db.getVersion() < 41) {
|
||||
try {
|
||||
db.execSQL("ALTER TABLE folders ADD integrate INTEGER");
|
||||
db.execSQL("ALTER TABLE folders ADD top_group INTEGER");
|
||||
db.execSQL("ALTER TABLE folders ADD poll_class TEXT");
|
||||
db.execSQL("ALTER TABLE folders ADD push_class TEXT");
|
||||
db.execSQL("ALTER TABLE folders ADD display_class TEXT");
|
||||
} catch (SQLiteException e) {
|
||||
if (! e.getMessage().startsWith("duplicate column name:")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
SharedPreferences prefs = this.localStore.getPreferences();
|
||||
cursor = db.rawQuery("SELECT id, name FROM folders", null);
|
||||
while (cursor.moveToNext()) {
|
||||
try {
|
||||
int id = cursor.getInt(0);
|
||||
String name = cursor.getString(1);
|
||||
update41Metadata(db, prefs, id, name);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, " error trying to ugpgrade a folder class", e);
|
||||
}
|
||||
}
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(K9.LOG_TAG, "Exception while upgrading database to v41. folder classes may have vanished", e);
|
||||
} finally {
|
||||
Utility.closeQuietly(cursor);
|
||||
}
|
||||
}
|
||||
if (db.getVersion() == 41) {
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
SharedPreferences.Editor editor = this.localStore.getPreferences().edit();
|
||||
|
||||
List <? extends Folder > folders = this.localStore.getPersonalNamespaces(true);
|
||||
for (Folder folder : folders) {
|
||||
if (folder instanceof LocalFolder) {
|
||||
LocalFolder lFolder = (LocalFolder)folder;
|
||||
lFolder.save(editor);
|
||||
}
|
||||
}
|
||||
|
||||
editor.commit();
|
||||
long endTime = System.currentTimeMillis();
|
||||
Log.i(K9.LOG_TAG, "Putting folder preferences for " + folders.size() + " folders back into Preferences took " + (endTime - startTime) + " ms");
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Could not replace Preferences in upgrade from DB_VERSION 41", e);
|
||||
}
|
||||
}
|
||||
if (db.getVersion() < 43) {
|
||||
try {
|
||||
// If folder "OUTBOX" (old, v3.800 - v3.802) exists, rename it to
|
||||
// "K9MAIL_INTERNAL_OUTBOX" (new)
|
||||
LocalFolder oldOutbox = new LocalFolder(this.localStore, "OUTBOX");
|
||||
if (oldOutbox.exists()) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("name", Account.OUTBOX);
|
||||
db.update("folders", cv, "name = ?", new String[] { "OUTBOX" });
|
||||
Log.i(K9.LOG_TAG, "Renamed folder OUTBOX to " + Account.OUTBOX);
|
||||
}
|
||||
|
||||
// Check if old (pre v3.800) localized outbox folder exists
|
||||
String localizedOutbox = K9.app.getString(R.string.special_mailbox_name_outbox);
|
||||
LocalFolder obsoleteOutbox = new LocalFolder(this.localStore, localizedOutbox);
|
||||
if (obsoleteOutbox.exists()) {
|
||||
// Get all messages from the localized outbox ...
|
||||
List<? extends Message> messages = obsoleteOutbox.getMessages(null, false);
|
||||
|
||||
if (messages.size() > 0) {
|
||||
// ... and move them to the drafts folder (we don't want to
|
||||
// surprise the user by sending potentially very old messages)
|
||||
LocalFolder drafts = new LocalFolder(this.localStore, this.localStore.getAccount().getDraftsFolderName());
|
||||
obsoleteOutbox.moveMessages(messages, drafts);
|
||||
}
|
||||
|
||||
// Now get rid of the localized outbox
|
||||
obsoleteOutbox.delete();
|
||||
obsoleteOutbox.delete(true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Error trying to fix the outbox folders", e);
|
||||
}
|
||||
}
|
||||
if (db.getVersion() < 44) {
|
||||
try {
|
||||
db.execSQL("ALTER TABLE messages ADD thread_root INTEGER");
|
||||
db.execSQL("ALTER TABLE messages ADD thread_parent INTEGER");
|
||||
db.execSQL("ALTER TABLE messages ADD normalized_subject_hash INTEGER");
|
||||
db.execSQL("ALTER TABLE messages ADD empty INTEGER");
|
||||
} catch (SQLiteException e) {
|
||||
if (! e.getMessage().startsWith("duplicate column name:")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (db.getVersion() < 45) {
|
||||
try {
|
||||
db.execSQL("DROP INDEX IF EXISTS msg_empty");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_empty ON messages (empty)");
|
||||
|
||||
db.execSQL("DROP INDEX IF EXISTS msg_thread_root");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_thread_root ON messages (thread_root)");
|
||||
|
||||
db.execSQL("DROP INDEX IF EXISTS msg_thread_parent");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_thread_parent ON messages (thread_parent)");
|
||||
} catch (SQLiteException e) {
|
||||
if (! e.getMessage().startsWith("duplicate column name:")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (db.getVersion() < 46) {
|
||||
db.execSQL("ALTER TABLE messages ADD read INTEGER default 0");
|
||||
db.execSQL("ALTER TABLE messages ADD flagged INTEGER default 0");
|
||||
db.execSQL("ALTER TABLE messages ADD answered INTEGER default 0");
|
||||
db.execSQL("ALTER TABLE messages ADD forwarded INTEGER default 0");
|
||||
|
||||
String[] projection = { "id", "flags" };
|
||||
|
||||
ContentValues cv = new ContentValues();
|
||||
List<Flag> extraFlags = new ArrayList<Flag>();
|
||||
|
||||
Cursor cursor = db.query("messages", projection, null, null, null, null, null);
|
||||
try {
|
||||
while (cursor.moveToNext()) {
|
||||
long id = cursor.getLong(0);
|
||||
String flagList = cursor.getString(1);
|
||||
|
||||
boolean read = false;
|
||||
boolean flagged = false;
|
||||
boolean answered = false;
|
||||
boolean forwarded = false;
|
||||
|
||||
if (flagList != null && flagList.length() > 0) {
|
||||
String[] flags = flagList.split(",");
|
||||
|
||||
for (String flagStr : flags) {
|
||||
try {
|
||||
Flag flag = Flag.valueOf(flagStr);
|
||||
|
||||
switch (flag) {
|
||||
case ANSWERED: {
|
||||
answered = true;
|
||||
break;
|
||||
}
|
||||
case DELETED: {
|
||||
// Don't store this in column 'flags'
|
||||
break;
|
||||
}
|
||||
case FLAGGED: {
|
||||
flagged = true;
|
||||
break;
|
||||
}
|
||||
case FORWARDED: {
|
||||
forwarded = true;
|
||||
break;
|
||||
}
|
||||
case SEEN: {
|
||||
read = true;
|
||||
break;
|
||||
}
|
||||
case DRAFT:
|
||||
case RECENT:
|
||||
case X_DESTROYED:
|
||||
case X_DOWNLOADED_FULL:
|
||||
case X_DOWNLOADED_PARTIAL:
|
||||
case X_GOT_ALL_HEADERS:
|
||||
case X_REMOTE_COPY_STARTED:
|
||||
case X_SEND_FAILED:
|
||||
case X_SEND_IN_PROGRESS: {
|
||||
extraFlags.add(flag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore bad flags
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cv.put("flags", this.localStore.serializeFlags(extraFlags));
|
||||
cv.put("read", read);
|
||||
cv.put("flagged", flagged);
|
||||
cv.put("answered", answered);
|
||||
cv.put("forwarded", forwarded);
|
||||
|
||||
db.update("messages", cv, "id = ?", new String[] { Long.toString(id) });
|
||||
|
||||
cv.clear();
|
||||
extraFlags.clear();
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_read ON messages (read)");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_flagged ON messages (flagged)");
|
||||
}
|
||||
|
||||
if (db.getVersion() < 47) {
|
||||
// Create new 'threads' table
|
||||
db.execSQL("DROP TABLE IF EXISTS threads");
|
||||
db.execSQL("CREATE TABLE threads (" +
|
||||
"id INTEGER PRIMARY KEY, " +
|
||||
"message_id INTEGER, " +
|
||||
"root INTEGER, " +
|
||||
"parent INTEGER" +
|
||||
")");
|
||||
|
||||
// Create indices for new table
|
||||
db.execSQL("DROP INDEX IF EXISTS threads_message_id");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS threads_message_id ON threads (message_id)");
|
||||
|
||||
db.execSQL("DROP INDEX IF EXISTS threads_root");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS threads_root ON threads (root)");
|
||||
|
||||
db.execSQL("DROP INDEX IF EXISTS threads_parent");
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS threads_parent ON threads (parent)");
|
||||
|
||||
// Create entries for all messages in 'threads' table
|
||||
db.execSQL("INSERT INTO threads (message_id) SELECT id FROM messages");
|
||||
|
||||
// Copy thread structure from 'messages' table to 'threads'
|
||||
Cursor cursor = db.query("messages",
|
||||
new String[] { "id", "thread_root", "thread_parent" },
|
||||
null, null, null, null, null);
|
||||
try {
|
||||
ContentValues cv = new ContentValues();
|
||||
while (cursor.moveToNext()) {
|
||||
cv.clear();
|
||||
long messageId = cursor.getLong(0);
|
||||
|
||||
if (!cursor.isNull(1)) {
|
||||
long threadRootMessageId = cursor.getLong(1);
|
||||
db.execSQL("UPDATE threads SET root = (SELECT t.id FROM " +
|
||||
"threads t WHERE t.message_id = ?) " +
|
||||
"WHERE message_id = ?",
|
||||
new String[] {
|
||||
Long.toString(threadRootMessageId),
|
||||
Long.toString(messageId)
|
||||
});
|
||||
}
|
||||
|
||||
if (!cursor.isNull(2)) {
|
||||
long threadParentMessageId = cursor.getLong(2);
|
||||
db.execSQL("UPDATE threads SET parent = (SELECT t.id FROM " +
|
||||
"threads t WHERE t.message_id = ?) " +
|
||||
"WHERE message_id = ?",
|
||||
new String[] {
|
||||
Long.toString(threadParentMessageId),
|
||||
Long.toString(messageId)
|
||||
});
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
// Remove indices for old thread-related columns in 'messages' table
|
||||
db.execSQL("DROP INDEX IF EXISTS msg_thread_root");
|
||||
db.execSQL("DROP INDEX IF EXISTS msg_thread_parent");
|
||||
|
||||
// Clear out old thread-related columns in 'messages'
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.putNull("thread_root");
|
||||
cv.putNull("thread_parent");
|
||||
db.update("messages", cv, null, null);
|
||||
}
|
||||
|
||||
if (db.getVersion() < 48) {
|
||||
db.execSQL("UPDATE threads SET root=id WHERE root IS NULL");
|
||||
|
||||
db.execSQL("CREATE TRIGGER set_thread_root " +
|
||||
"AFTER INSERT ON threads " +
|
||||
"BEGIN " +
|
||||
"UPDATE threads SET root=id WHERE root IS NULL AND ROWID = NEW.ROWID; " +
|
||||
"END");
|
||||
}
|
||||
if (db.getVersion() < 49) {
|
||||
db.execSQL("CREATE INDEX IF NOT EXISTS msg_composite ON messages (deleted, empty,folder_id,flagged,read)");
|
||||
|
||||
}
|
||||
if (db.getVersion() < 50) {
|
||||
try {
|
||||
db.execSQL("ALTER TABLE folders ADD notify_class TEXT default '" +
|
||||
Folder.FolderClass.INHERITED.name() + "'");
|
||||
} catch (SQLiteException e) {
|
||||
if (! e.getMessage().startsWith("duplicate column name:")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("notify_class", Folder.FolderClass.FIRST_CLASS.name());
|
||||
|
||||
db.update("folders", cv, "name = ?",
|
||||
new String[] { this.localStore.getAccount().getInboxFolderName() });
|
||||
}
|
||||
}
|
||||
|
||||
db.setVersion(LocalStore.DB_VERSION);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
if (db.getVersion() != LocalStore.DB_VERSION) {
|
||||
throw new RuntimeException("Database upgrade failed!");
|
||||
}
|
||||
}
|
||||
|
||||
private void update41Metadata(final SQLiteDatabase db, SharedPreferences prefs, int id, String name) {
|
||||
|
||||
|
||||
Folder.FolderClass displayClass = Folder.FolderClass.NO_CLASS;
|
||||
Folder.FolderClass syncClass = Folder.FolderClass.INHERITED;
|
||||
Folder.FolderClass pushClass = Folder.FolderClass.SECOND_CLASS;
|
||||
boolean inTopGroup = false;
|
||||
boolean integrate = false;
|
||||
if (this.localStore.getAccount().getInboxFolderName().equals(name)) {
|
||||
displayClass = Folder.FolderClass.FIRST_CLASS;
|
||||
syncClass = Folder.FolderClass.FIRST_CLASS;
|
||||
pushClass = Folder.FolderClass.FIRST_CLASS;
|
||||
inTopGroup = true;
|
||||
integrate = true;
|
||||
}
|
||||
|
||||
try {
|
||||
displayClass = Folder.FolderClass.valueOf(prefs.getString(this.localStore.uUid + "." + name + ".displayMode", displayClass.name()));
|
||||
syncClass = Folder.FolderClass.valueOf(prefs.getString(this.localStore.uUid + "." + name + ".syncMode", syncClass.name()));
|
||||
pushClass = Folder.FolderClass.valueOf(prefs.getString(this.localStore.uUid + "." + name + ".pushMode", pushClass.name()));
|
||||
inTopGroup = prefs.getBoolean(this.localStore.uUid + "." + name + ".inTopGroup", inTopGroup);
|
||||
integrate = prefs.getBoolean(this.localStore.uUid + "." + name + ".integrate", integrate);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, " Throwing away an error while trying to upgrade folder metadata", e);
|
||||
}
|
||||
|
||||
if (displayClass == Folder.FolderClass.NONE) {
|
||||
displayClass = Folder.FolderClass.NO_CLASS;
|
||||
}
|
||||
if (syncClass == Folder.FolderClass.NONE) {
|
||||
syncClass = Folder.FolderClass.INHERITED;
|
||||
}
|
||||
if (pushClass == Folder.FolderClass.NONE) {
|
||||
pushClass = Folder.FolderClass.INHERITED;
|
||||
}
|
||||
|
||||
db.execSQL("UPDATE folders SET integrate = ?, top_group = ?, poll_class=?, push_class =?, display_class = ? WHERE id = ?",
|
||||
new Object[] { integrate, inTopGroup, syncClass, pushClass, displayClass, id });
|
||||
|
||||
}
|
||||
}
|
29
src/com/fsck/k9/mail/store/local/TempFileBody.java
Normal file
29
src/com/fsck/k9/mail/store/local/TempFileBody.java
Normal file
@ -0,0 +1,29 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
|
||||
/**
|
||||
* An attachment whose contents are contained in a file.
|
||||
*/
|
||||
public class TempFileBody extends BinaryAttachmentBody {
|
||||
private final File mFile;
|
||||
|
||||
public TempFileBody(String filename) {
|
||||
mFile = new File(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws MessagingException {
|
||||
try {
|
||||
return new FileInputStream(mFile);
|
||||
} catch (FileNotFoundException e) {
|
||||
return new ByteArrayInputStream(LocalStore.EMPTY_BYTE_ARRAY);
|
||||
}
|
||||
}
|
||||
}
|
40
src/com/fsck/k9/mail/store/local/TempFileMessageBody.java
Normal file
40
src/com/fsck/k9/mail/store/local/TempFileMessageBody.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.james.mime4j.util.MimeUtil;
|
||||
|
||||
import com.fsck.k9.mail.CompositeBody;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
|
||||
/**
|
||||
* An attachment containing a body of type message/rfc822
|
||||
* whose contents are contained in a file.
|
||||
*/
|
||||
public class TempFileMessageBody extends TempFileBody implements CompositeBody {
|
||||
|
||||
public TempFileMessageBody(String filename) {
|
||||
super(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||
AttachmentMessageBodyUtil.writeTo(this, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsing7bitTransport() throws MessagingException {
|
||||
// see LocalAttachmentMessageBody.setUsing7bitTransport()
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncoding(String encoding) throws MessagingException {
|
||||
if (!MimeUtil.ENC_7BIT.equalsIgnoreCase(encoding)
|
||||
&& !MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) {
|
||||
throw new MessagingException(
|
||||
"Incompatible content-transfer-encoding applied to a CompositeBody");
|
||||
}
|
||||
mEncoding = encoding;
|
||||
}
|
||||
}
|
17
src/com/fsck/k9/mail/store/local/ThreadInfo.java
Normal file
17
src/com/fsck/k9/mail/store/local/ThreadInfo.java
Normal file
@ -0,0 +1,17 @@
|
||||
package com.fsck.k9.mail.store.local;
|
||||
|
||||
class ThreadInfo {
|
||||
public final long threadId;
|
||||
public final long msgId;
|
||||
public final String messageId;
|
||||
public final long rootId;
|
||||
public final long parentId;
|
||||
|
||||
public ThreadInfo(long threadId, long msgId, String messageId, long rootId, long parentId) {
|
||||
this.threadId = threadId;
|
||||
this.msgId = msgId;
|
||||
this.messageId = messageId;
|
||||
this.rootId = rootId;
|
||||
this.parentId = parentId;
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import android.util.Log;
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.helper.UrlEncodingHelper;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.*;
|
||||
import com.fsck.k9.mail.Message.RecipientType;
|
||||
@ -14,7 +15,7 @@ import com.fsck.k9.mail.filter.LineWrapOutputStream;
|
||||
import com.fsck.k9.mail.filter.PeekableInputStream;
|
||||
import com.fsck.k9.mail.filter.SmtpDataStuffing;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
||||
import com.fsck.k9.mail.store.local.LocalMessage;
|
||||
import com.fsck.k9.net.ssl.TrustedSocketFactory;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
@ -23,7 +24,6 @@ import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.*;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.*;
|
||||
@ -92,28 +92,23 @@ public class SmtpTransport extends Transport {
|
||||
}
|
||||
|
||||
if (smtpUri.getUserInfo() != null) {
|
||||
try {
|
||||
String[] userInfoParts = smtpUri.getUserInfo().split(":");
|
||||
if (userInfoParts.length == 1) {
|
||||
authType = AuthType.PLAIN;
|
||||
username = URLDecoder.decode(userInfoParts[0], "UTF-8");
|
||||
} else if (userInfoParts.length == 2) {
|
||||
authType = AuthType.PLAIN;
|
||||
username = URLDecoder.decode(userInfoParts[0], "UTF-8");
|
||||
password = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
} else if (userInfoParts.length == 3) {
|
||||
// NOTE: In SmptTransport URIs, the authType comes last!
|
||||
authType = AuthType.valueOf(userInfoParts[2]);
|
||||
username = URLDecoder.decode(userInfoParts[0], "UTF-8");
|
||||
if (authType == AuthType.EXTERNAL) {
|
||||
clientCertificateAlias = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
} else {
|
||||
password = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
}
|
||||
String[] userInfoParts = smtpUri.getUserInfo().split(":");
|
||||
if (userInfoParts.length == 1) {
|
||||
authType = AuthType.PLAIN;
|
||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]);
|
||||
} else if (userInfoParts.length == 2) {
|
||||
authType = AuthType.PLAIN;
|
||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]);
|
||||
password = UrlEncodingHelper.decodeUtf8(userInfoParts[1]);
|
||||
} else if (userInfoParts.length == 3) {
|
||||
// NOTE: In SmptTransport URIs, the authType comes last!
|
||||
authType = AuthType.valueOf(userInfoParts[2]);
|
||||
username = UrlEncodingHelper.decodeUtf8(userInfoParts[0]);
|
||||
if (authType == AuthType.EXTERNAL) {
|
||||
clientCertificateAlias = UrlEncodingHelper.decodeUtf8(userInfoParts[1]);
|
||||
} else {
|
||||
password = UrlEncodingHelper.decodeUtf8(userInfoParts[1]);
|
||||
}
|
||||
} catch (UnsupportedEncodingException enc) {
|
||||
// This shouldn't happen since the encoding is hardcoded to UTF-8
|
||||
throw new IllegalArgumentException("Couldn't urldecode username or password.", enc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,20 +128,12 @@ public class SmtpTransport extends Transport {
|
||||
* @see SmtpTransport#decodeUri(String)
|
||||
*/
|
||||
public static String createUri(ServerSettings server) {
|
||||
String userEnc;
|
||||
String passwordEnc;
|
||||
String clientCertificateAliasEnc;
|
||||
try {
|
||||
userEnc = (server.username != null) ?
|
||||
URLEncoder.encode(server.username, "UTF-8") : "";
|
||||
passwordEnc = (server.password != null) ?
|
||||
URLEncoder.encode(server.password, "UTF-8") : "";
|
||||
clientCertificateAliasEnc = (server.clientCertificateAlias != null) ?
|
||||
URLEncoder.encode(server.clientCertificateAlias, "UTF-8") : "";
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalArgumentException("Could not encode username or password", e);
|
||||
}
|
||||
String userEnc = (server.username != null) ?
|
||||
UrlEncodingHelper.encodeUtf8(server.username) : "";
|
||||
String passwordEnc = (server.password != null) ?
|
||||
UrlEncodingHelper.encodeUtf8(server.password) : "";
|
||||
String clientCertificateAliasEnc = (server.clientCertificateAlias != null) ?
|
||||
UrlEncodingHelper.encodeUtf8(server.clientCertificateAlias) : "";
|
||||
|
||||
String scheme;
|
||||
switch (server.connectionSecurity) {
|
||||
@ -183,16 +170,16 @@ public class SmtpTransport extends Transport {
|
||||
}
|
||||
|
||||
|
||||
String mHost;
|
||||
int mPort;
|
||||
String mUsername;
|
||||
String mPassword;
|
||||
String mClientCertificateAlias;
|
||||
AuthType mAuthType;
|
||||
ConnectionSecurity mConnectionSecurity;
|
||||
Socket mSocket;
|
||||
PeekableInputStream mIn;
|
||||
OutputStream mOut;
|
||||
private String mHost;
|
||||
private int mPort;
|
||||
private String mUsername;
|
||||
private String mPassword;
|
||||
private String mClientCertificateAlias;
|
||||
private AuthType mAuthType;
|
||||
private ConnectionSecurity mConnectionSecurity;
|
||||
private Socket mSocket;
|
||||
private PeekableInputStream mIn;
|
||||
private OutputStream mOut;
|
||||
private boolean m8bitEncodingAllowed;
|
||||
private int mLargestAcceptableMessage;
|
||||
|
||||
@ -269,7 +256,7 @@ public class SmtpTransport extends Transport {
|
||||
}
|
||||
}
|
||||
|
||||
HashMap<String,String> extensions = sendHello(localHost);
|
||||
Map<String,String> extensions = sendHello(localHost);
|
||||
|
||||
m8bitEncodingAllowed = extensions.containsKey("8BITMIME");
|
||||
|
||||
@ -432,7 +419,7 @@ public class SmtpTransport extends Transport {
|
||||
* @param host
|
||||
* The EHLO/HELO parameter as defined by the RFC.
|
||||
*
|
||||
* @return A (possibly empty) {@code HashMap<String,String>} of extensions (upper case) and
|
||||
* @return A (possibly empty) {@code Map<String,String>} of extensions (upper case) and
|
||||
* their parameters (possibly 0 length) as returned by the EHLO command
|
||||
*
|
||||
* @throws IOException
|
||||
@ -440,8 +427,8 @@ public class SmtpTransport extends Transport {
|
||||
* @throws MessagingException
|
||||
* In case of a malformed response.
|
||||
*/
|
||||
private HashMap<String,String> sendHello(String host) throws IOException, MessagingException {
|
||||
HashMap<String, String> extensions = new HashMap<String, String>();
|
||||
private Map<String,String> sendHello(String host) throws IOException, MessagingException {
|
||||
Map<String, String> extensions = new HashMap<String, String>();
|
||||
try {
|
||||
List<String> results = executeSimpleCommand("EHLO " + host);
|
||||
// Remove the EHLO greeting response
|
||||
@ -466,7 +453,7 @@ public class SmtpTransport extends Transport {
|
||||
|
||||
@Override
|
||||
public void sendMessage(Message message) throws MessagingException {
|
||||
ArrayList<Address> addresses = new ArrayList<Address>();
|
||||
List<Address> addresses = new ArrayList<Address>();
|
||||
{
|
||||
addresses.addAll(Arrays.asList(message.getRecipients(RecipientType.TO)));
|
||||
addresses.addAll(Arrays.asList(message.getRecipients(RecipientType.CC)));
|
||||
@ -474,12 +461,12 @@ public class SmtpTransport extends Transport {
|
||||
}
|
||||
message.setRecipients(RecipientType.BCC, null);
|
||||
|
||||
HashMap<String, ArrayList<String>> charsetAddressesMap =
|
||||
new HashMap<String, ArrayList<String>>();
|
||||
Map<String, List<String>> charsetAddressesMap =
|
||||
new HashMap<String, List<String>>();
|
||||
for (Address address : addresses) {
|
||||
String addressString = address.getAddress();
|
||||
String charset = MimeUtility.getCharsetFromAddress(addressString);
|
||||
ArrayList<String> addressesOfCharset = charsetAddressesMap.get(charset);
|
||||
List<String> addressesOfCharset = charsetAddressesMap.get(charset);
|
||||
if (addressesOfCharset == null) {
|
||||
addressesOfCharset = new ArrayList<String>();
|
||||
charsetAddressesMap.put(charset, addressesOfCharset);
|
||||
@ -487,16 +474,16 @@ public class SmtpTransport extends Transport {
|
||||
addressesOfCharset.add(addressString);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, ArrayList<String>> charsetAddressesMapEntry :
|
||||
for (Map.Entry<String, List<String>> charsetAddressesMapEntry :
|
||||
charsetAddressesMap.entrySet()) {
|
||||
String charset = charsetAddressesMapEntry.getKey();
|
||||
ArrayList<String> addressesOfCharset = charsetAddressesMapEntry.getValue();
|
||||
List<String> addressesOfCharset = charsetAddressesMapEntry.getValue();
|
||||
message.setCharset(charset);
|
||||
sendMessageTo(addressesOfCharset, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessageTo(ArrayList<String> addresses, Message message)
|
||||
private void sendMessageTo(List<String> addresses, Message message)
|
||||
throws MessagingException {
|
||||
boolean possibleSend = false;
|
||||
|
||||
@ -511,6 +498,7 @@ public class SmtpTransport extends Transport {
|
||||
if (mLargestAcceptableMessage > 0 && ((LocalMessage)message).hasAttachments()) {
|
||||
if (message.calculateSize() > mLargestAcceptableMessage) {
|
||||
MessagingException me = new MessagingException("Message too large for server");
|
||||
//TODO this looks rather suspicious... shouldn't it be true?
|
||||
me.setPermanentFailure(possibleSend);
|
||||
throw me;
|
||||
}
|
||||
@ -545,14 +533,13 @@ public class SmtpTransport extends Transport {
|
||||
possibleSend = false;
|
||||
}
|
||||
|
||||
//TODO this looks rather suspicious... why is possibleSend used, and why are 5xx NOT permanent (in contrast to the log text)
|
||||
me.setPermanentFailure(possibleSend);
|
||||
throw me;
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -11,6 +11,8 @@ import com.fsck.k9.mail.ServerSettings;
|
||||
import com.fsck.k9.mail.Transport;
|
||||
import com.fsck.k9.mail.store.WebDavStore;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class WebDavTransport extends Transport {
|
||||
public static final String TRANSPORT_TYPE = WebDavStore.STORE_TYPE;
|
||||
|
||||
@ -66,6 +68,6 @@ public class WebDavTransport extends Transport {
|
||||
|
||||
@Override
|
||||
public void sendMessage(Message message) throws MessagingException {
|
||||
store.sendMessages(new Message[] { message });
|
||||
store.sendMessages(Collections.singletonList(message));
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,8 @@ public final class TrustManagerFactory {
|
||||
String message = null;
|
||||
X509Certificate certificate = chain[0];
|
||||
|
||||
Throwable cause = null;
|
||||
|
||||
try {
|
||||
defaultTrustManager.checkServerTrusted(chain, authType);
|
||||
new StrictHostnameVerifier().verify(mHost, certificate);
|
||||
@ -70,15 +72,17 @@ public final class TrustManagerFactory {
|
||||
} catch (CertificateException e) {
|
||||
// cert. chain can't be validated
|
||||
message = e.getMessage();
|
||||
cause = e;
|
||||
} catch (SSLException e) {
|
||||
// host name doesn't match certificate
|
||||
message = e.getMessage();
|
||||
cause = e;
|
||||
}
|
||||
|
||||
// Check the local key store if we couldn't verify the certificate using the global
|
||||
// key store or if the host name doesn't match the certificate name
|
||||
if (!keyStore.isValidCertificate(certificate, mHost, mPort)) {
|
||||
throw new CertificateChainException(message, chain);
|
||||
throw new CertificateChainException(message, chain, cause);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,15 @@ import com.fsck.k9.K9;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
public class Editor implements android.content.SharedPreferences.Editor {
|
||||
private Storage storage;
|
||||
private HashMap<String, String> changes = new HashMap<String, String>();
|
||||
private ArrayList<String> removals = new ArrayList<String>();
|
||||
private Map<String, String> changes = new HashMap<String, String>();
|
||||
private List<String> removals = new ArrayList<String>();
|
||||
private boolean removeAll = false;
|
||||
|
||||
Map<String, String> snapshot = new HashMap<String, String>();
|
||||
|
@ -51,7 +51,7 @@ public class Settings {
|
||||
SortedMap<Integer, SettingsDescription> headMap = versions.headMap(version + 1);
|
||||
|
||||
// Skip this setting if it was introduced after 'version'
|
||||
if (headMap.size() == 0) {
|
||||
if (headMap.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ public class SettingsExporter {
|
||||
|
||||
Set<String> exportAccounts;
|
||||
if (accountUuids == null) {
|
||||
Account[] accounts = preferences.getAccounts();
|
||||
List<Account> accounts = preferences.getAccounts();
|
||||
exportAccounts = new HashSet<String>();
|
||||
for (Account account : accounts) {
|
||||
exportAccounts.add(account.getUuid());
|
||||
|
@ -340,7 +340,7 @@ public class SettingsImporter {
|
||||
AccountDescription original = new AccountDescription(account.name, account.uuid);
|
||||
|
||||
Preferences prefs = Preferences.getPreferences(context);
|
||||
Account[] accounts = prefs.getAccounts();
|
||||
List<Account> accounts = prefs.getAccounts();
|
||||
|
||||
String uuid = account.uuid;
|
||||
Account existingAccount = prefs.getAccount(uuid);
|
||||
@ -357,7 +357,7 @@ public class SettingsImporter {
|
||||
if (isAccountNameUsed(accountName, accounts)) {
|
||||
// Account name is already in use. So generate a new one by appending " (x)", where x
|
||||
// is the first number >= 1 that results in an unused account name.
|
||||
for (int i = 1; i <= accounts.length; i++) {
|
||||
for (int i = 1; i <= accounts.size(); i++) {
|
||||
accountName = account.name + " (" + i + ")";
|
||||
if (!isAccountNameUsed(accountName, accounts)) {
|
||||
break;
|
||||
@ -605,7 +605,7 @@ public class SettingsImporter {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAccountNameUsed(String name, Account[] accounts) {
|
||||
private static boolean isAccountNameUsed(String name, List<Account> accounts) {
|
||||
for (Account account : accounts) {
|
||||
if (account == null) {
|
||||
continue;
|
||||
|
@ -9,21 +9,24 @@ import android.database.sqlite.SQLiteStatement;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.helper.UrlEncodingHelper;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class Storage implements SharedPreferences {
|
||||
private static ConcurrentHashMap<Context, Storage> storages =
|
||||
private static ConcurrentMap<Context, Storage> storages =
|
||||
new ConcurrentHashMap<Context, Storage>();
|
||||
|
||||
private volatile ConcurrentHashMap<String, String> storage = new ConcurrentHashMap<String, String>();
|
||||
private volatile ConcurrentMap<String, String> storage = new ConcurrentHashMap<String, String>();
|
||||
|
||||
private CopyOnWriteArrayList<OnSharedPreferenceChangeListener> listeners =
|
||||
new CopyOnWriteArrayList<OnSharedPreferenceChangeListener>();
|
||||
@ -31,11 +34,11 @@ public class Storage implements SharedPreferences {
|
||||
private int DB_VERSION = 2;
|
||||
private String DB_NAME = "preferences_storage";
|
||||
|
||||
private ThreadLocal<ConcurrentHashMap<String, String>> workingStorage
|
||||
= new ThreadLocal<ConcurrentHashMap<String, String>>();
|
||||
private ThreadLocal<ConcurrentMap<String, String>> workingStorage
|
||||
= new ThreadLocal<ConcurrentMap<String, String>>();
|
||||
private ThreadLocal<SQLiteDatabase> workingDB =
|
||||
new ThreadLocal<SQLiteDatabase>();
|
||||
private ThreadLocal<ArrayList<String>> workingChangedKeys = new ThreadLocal<ArrayList<String>>();
|
||||
private ThreadLocal<List<String>> workingChangedKeys = new ThreadLocal<List<String>>();
|
||||
|
||||
|
||||
private Context context = null;
|
||||
@ -59,11 +62,11 @@ public class Storage implements SharedPreferences {
|
||||
if (transportUriStr != null) {
|
||||
String[] userInfoParts = uri.getUserInfo().split(":");
|
||||
|
||||
String usernameEnc = URLEncoder.encode(userInfoParts[0], "UTF-8");
|
||||
String usernameEnc = UrlEncodingHelper.encodeUtf8(userInfoParts[0]);
|
||||
String passwordEnc = "";
|
||||
String authType = "";
|
||||
if (userInfoParts.length > 1) {
|
||||
passwordEnc = ":" + URLEncoder.encode(userInfoParts[1], "UTF-8");
|
||||
passwordEnc = ":" + UrlEncodingHelper.encodeUtf8(userInfoParts[1]);
|
||||
}
|
||||
if (userInfoParts.length > 2) {
|
||||
authType = ":" + userInfoParts[2];
|
||||
@ -83,34 +86,34 @@ public class Storage implements SharedPreferences {
|
||||
if (storeUriStr.startsWith("imap")) {
|
||||
String[] userInfoParts = uri.getUserInfo().split(":");
|
||||
if (userInfoParts.length == 2) {
|
||||
String usernameEnc = URLEncoder.encode(userInfoParts[0], "UTF-8");
|
||||
String passwordEnc = URLEncoder.encode(userInfoParts[1], "UTF-8");
|
||||
String usernameEnc = UrlEncodingHelper.encodeUtf8(userInfoParts[0]);
|
||||
String passwordEnc = UrlEncodingHelper.encodeUtf8(userInfoParts[1]);
|
||||
|
||||
newUserInfo = usernameEnc + ":" + passwordEnc;
|
||||
} else {
|
||||
String authType = userInfoParts[0];
|
||||
String usernameEnc = URLEncoder.encode(userInfoParts[1], "UTF-8");
|
||||
String passwordEnc = URLEncoder.encode(userInfoParts[2], "UTF-8");
|
||||
String usernameEnc = UrlEncodingHelper.encodeUtf8(userInfoParts[1]);
|
||||
String passwordEnc = UrlEncodingHelper.encodeUtf8(userInfoParts[2]);
|
||||
|
||||
newUserInfo = authType + ":" + usernameEnc + ":" + passwordEnc;
|
||||
}
|
||||
} else if (storeUriStr.startsWith("pop3")) {
|
||||
String[] userInfoParts = uri.getUserInfo().split(":", 2);
|
||||
String usernameEnc = URLEncoder.encode(userInfoParts[0], "UTF-8");
|
||||
String usernameEnc = UrlEncodingHelper.encodeUtf8(userInfoParts[0]);
|
||||
|
||||
String passwordEnc = "";
|
||||
if (userInfoParts.length > 1) {
|
||||
passwordEnc = ":" + URLEncoder.encode(userInfoParts[1], "UTF-8");
|
||||
passwordEnc = ":" + UrlEncodingHelper.encodeUtf8(userInfoParts[1]);
|
||||
}
|
||||
|
||||
newUserInfo = usernameEnc + passwordEnc;
|
||||
} else if (storeUriStr.startsWith("webdav")) {
|
||||
String[] userInfoParts = uri.getUserInfo().split(":", 2);
|
||||
String usernameEnc = URLEncoder.encode(userInfoParts[0], "UTF-8");
|
||||
String usernameEnc = UrlEncodingHelper.encodeUtf8(userInfoParts[0]);
|
||||
|
||||
String passwordEnc = "";
|
||||
if (userInfoParts.length > 1) {
|
||||
passwordEnc = ":" + URLEncoder.encode(userInfoParts[1], "UTF-8");
|
||||
passwordEnc = ":" + UrlEncodingHelper.encodeUtf8(userInfoParts[1]);
|
||||
}
|
||||
|
||||
newUserInfo = usernameEnc + passwordEnc;
|
||||
@ -201,7 +204,7 @@ public class Storage implements SharedPreferences {
|
||||
}
|
||||
|
||||
private void keyChange(String key) {
|
||||
ArrayList<String> changedKeys = workingChangedKeys.get();
|
||||
List<String> changedKeys = workingChangedKeys.get();
|
||||
if (!changedKeys.contains(key)) {
|
||||
changedKeys.add(key);
|
||||
}
|
||||
@ -258,14 +261,14 @@ public class Storage implements SharedPreferences {
|
||||
}
|
||||
|
||||
protected void doInTransaction(Runnable dbWork) {
|
||||
ConcurrentHashMap<String, String> newStorage = new ConcurrentHashMap<String, String>();
|
||||
ConcurrentMap<String, String> newStorage = new ConcurrentHashMap<String, String>();
|
||||
newStorage.putAll(storage);
|
||||
workingStorage.set(newStorage);
|
||||
|
||||
SQLiteDatabase mDb = openDB();
|
||||
workingDB.set(mDb);
|
||||
|
||||
ArrayList<String> changedKeys = new ArrayList<String>();
|
||||
List<String> changedKeys = new ArrayList<String>();
|
||||
workingChangedKeys.set(changedKeys);
|
||||
|
||||
mDb.beginTransaction();
|
||||
@ -287,13 +290,17 @@ public class Storage implements SharedPreferences {
|
||||
}
|
||||
}
|
||||
|
||||
public long size() {
|
||||
return storage.size();
|
||||
public boolean isEmpty() {
|
||||
return storage.isEmpty();
|
||||
}
|
||||
|
||||
//@Override
|
||||
public boolean contains(String key) {
|
||||
return storage.contains(key);
|
||||
// TODO this used to be ConcurrentHashMap#contains which is
|
||||
// actually containsValue. But looking at the usage of this method,
|
||||
// it's clear that containsKey is what's intended. Investigate if this
|
||||
// was a bug previously. Looks like it was only used once, when upgrading
|
||||
return storage.containsKey(key);
|
||||
}
|
||||
|
||||
//@Override
|
||||
|
@ -15,8 +15,8 @@ import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.LocalStore.AttachmentInfo;
|
||||
import com.fsck.k9.mail.store.local.LocalStore;
|
||||
import com.fsck.k9.mail.store.local.LocalStore.AttachmentInfo;
|
||||
import com.fsck.k9.mail.store.StorageManager;
|
||||
|
||||
import java.io.*;
|
||||
|
@ -11,10 +11,10 @@ import com.fsck.k9.cache.EmailProviderCacheCursor;
|
||||
import com.fsck.k9.helper.StringUtils;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.LockableDatabase;
|
||||
import com.fsck.k9.mail.store.LockableDatabase.DbCallback;
|
||||
import com.fsck.k9.mail.store.LockableDatabase.WrappedException;
|
||||
import com.fsck.k9.mail.store.local.LocalStore;
|
||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
||||
import com.fsck.k9.search.SqlQueryBuilder;
|
||||
|
||||
|
@ -32,7 +32,8 @@ import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.Folder;
|
||||
import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.local.LocalMessage;
|
||||
import com.fsck.k9.mail.store.local.LocalStore;
|
||||
import com.fsck.k9.search.SearchAccount;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
@ -152,9 +153,9 @@ public class MessageProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the {@link LocalStore.LocalMessage#getId() ID} from the given
|
||||
* Extracts the {@link LocalMessage#getId() ID} from the given
|
||||
* {@link MessageInfoHolder}. The underlying {@link Message} is expected to
|
||||
* be a {@link LocalStore.LocalMessage}.
|
||||
* be a {@link LocalMessage}.
|
||||
*/
|
||||
public static class IdExtractor implements FieldExtractor<MessageInfoHolder, Long> {
|
||||
@Override
|
||||
|
@ -54,7 +54,7 @@ public class ConditionsTreeNode implements Parcelable {
|
||||
* should point to rows representing the nodes of the tree.
|
||||
*
|
||||
* @param cursor Cursor pointing to the first of a bunch or rows. Each rows
|
||||
* should contains 1 tree node.
|
||||
* should contains 1 tree node.
|
||||
* @return A condition tree.
|
||||
*/
|
||||
public static ConditionsTreeNode buildTreeFromDB(Cursor cursor) {
|
||||
@ -256,7 +256,7 @@ public class ConditionsTreeNode implements Parcelable {
|
||||
* @return List of all nodes in subtree in preorder.
|
||||
*/
|
||||
public List<ConditionsTreeNode> preorder() {
|
||||
ArrayList<ConditionsTreeNode> result = new ArrayList<ConditionsTreeNode>();
|
||||
List<ConditionsTreeNode> result = new ArrayList<ConditionsTreeNode>();
|
||||
Stack<ConditionsTreeNode> stack = new Stack<ConditionsTreeNode>();
|
||||
stack.push(this);
|
||||
|
||||
|
@ -240,8 +240,8 @@ public class LocalSearch implements SearchSpecification {
|
||||
public void addAllowedFolder(String name) {
|
||||
/*
|
||||
* TODO find folder sub-tree
|
||||
* - do and on root of it & rest of search
|
||||
* - do or between folder nodes
|
||||
* - do and on root of it & rest of search
|
||||
* - do or between folder nodes
|
||||
*/
|
||||
mConditions = and(new SearchCondition(Searchfield.FOLDER, Attribute.EQUALS, name));
|
||||
}
|
||||
@ -252,7 +252,7 @@ public class LocalSearch implements SearchSpecification {
|
||||
* real searches because of possible extra conditions to a folder requirement.
|
||||
*/
|
||||
public List<String> getFolderNames() {
|
||||
ArrayList<String> results = new ArrayList<String>();
|
||||
List<String> results = new ArrayList<String>();
|
||||
for (ConditionsTreeNode node : mLeafSet) {
|
||||
if (node.mCondition.field == Searchfield.FOLDER &&
|
||||
node.mCondition.attribute == Attribute.EQUALS) {
|
||||
@ -328,7 +328,7 @@ public class LocalSearch implements SearchSpecification {
|
||||
*/
|
||||
@Override
|
||||
public String[] getAccountUuids() {
|
||||
if (mAccountUuids.size() == 0) {
|
||||
if (mAccountUuids.isEmpty()) {
|
||||
return new String[] { SearchSpecification.ALL_ACCOUNTS };
|
||||
}
|
||||
|
||||
@ -343,7 +343,7 @@ public class LocalSearch implements SearchSpecification {
|
||||
* @return {@code true} if all accounts should be searched.
|
||||
*/
|
||||
public boolean searchAllAccounts() {
|
||||
return (mAccountUuids.size() == 0);
|
||||
return (mAccountUuids.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,8 +58,8 @@ public interface SearchSpecification extends Parcelable {
|
||||
* By result, only the fields in here are searchable.
|
||||
*
|
||||
* Fields not in here at this moment ( and by effect not searchable ):
|
||||
* id, html_content, internal_date, message_id,
|
||||
* preview, mime_type
|
||||
* id, html_content, internal_date, message_id,
|
||||
* preview, mime_type
|
||||
*
|
||||
*/
|
||||
public enum Searchfield {
|
||||
@ -92,9 +92,9 @@ public interface SearchSpecification extends Parcelable {
|
||||
/**
|
||||
* This class represents 1 value for a certain search field. One
|
||||
* value consists of three things:
|
||||
* an attribute: equals, starts with, contains,...
|
||||
* a searchfield: date, flags, sender, subject,...
|
||||
* a value: "apple", "jesse",..
|
||||
* an attribute: equals, starts with, contains,...
|
||||
* a searchfield: date, flags, sender, subject,...
|
||||
* a value: "apple", "jesse",..
|
||||
*
|
||||
* @author dzan
|
||||
*/
|
||||
|
@ -5,8 +5,8 @@ import java.util.List;
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Folder;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
||||
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||
import com.fsck.k9.mail.store.local.LocalStore;
|
||||
import com.fsck.k9.search.SearchSpecification.Attribute;
|
||||
import com.fsck.k9.search.SearchSpecification.SearchCondition;
|
||||
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.fsck.k9.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import android.app.Service;
|
||||
@ -186,8 +187,8 @@ public class DatabaseUpgradeService extends Service {
|
||||
private void upgradeDatabases() {
|
||||
Preferences preferences = Preferences.getPreferences(this);
|
||||
|
||||
Account[] accounts = preferences.getAccounts();
|
||||
mProgressEnd = accounts.length;
|
||||
List<Account> accounts = preferences.getAccounts();
|
||||
mProgressEnd = accounts.size();
|
||||
mProgress = 0;
|
||||
|
||||
for (Account account : accounts) {
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.fsck.k9.service;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
@ -8,6 +10,7 @@ import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.activity.MessageCompose;
|
||||
import com.fsck.k9.activity.MessageReference;
|
||||
import com.fsck.k9.controller.MessagingController;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.Message;
|
||||
|
||||
@ -35,8 +38,7 @@ public class NotificationActionService extends CoreService {
|
||||
return PendingIntent.getService(context, account.getAccountNumber(), i, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
public static PendingIntent getReadAllMessagesIntent(Context context, final Account account,
|
||||
final ArrayList<MessageReference> refs) {
|
||||
public static PendingIntent getReadAllMessagesIntent(Context context, final Account account, final Serializable refs) {
|
||||
Intent i = new Intent(context, NotificationActionService.class);
|
||||
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
|
||||
i.putExtra(EXTRA_MESSAGE_LIST, refs);
|
||||
@ -53,8 +55,7 @@ public class NotificationActionService extends CoreService {
|
||||
return PendingIntent.getService(context, account.getAccountNumber(), i, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
public static Intent getDeleteAllMessagesIntent(Context context, final Account account,
|
||||
final ArrayList<MessageReference> refs) {
|
||||
public static Intent getDeleteAllMessagesIntent(Context context, final Account account, final Serializable refs) {
|
||||
Intent i = new Intent(context, NotificationActionService.class);
|
||||
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
|
||||
i.putExtra(EXTRA_MESSAGE_LIST, refs);
|
||||
@ -77,7 +78,7 @@ public class NotificationActionService extends CoreService {
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "NotificationActionService marking messages as read");
|
||||
|
||||
ArrayList<MessageReference> refs =
|
||||
List<MessageReference> refs =
|
||||
intent.getParcelableArrayListExtra(EXTRA_MESSAGE_LIST);
|
||||
for (MessageReference ref : refs) {
|
||||
controller.setFlag(account, ref.folderName, ref.uid, Flag.SEEN, true);
|
||||
@ -86,9 +87,9 @@ public class NotificationActionService extends CoreService {
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "NotificationActionService deleting messages");
|
||||
|
||||
ArrayList<MessageReference> refs =
|
||||
List<MessageReference> refs =
|
||||
intent.getParcelableArrayListExtra(EXTRA_MESSAGE_LIST);
|
||||
ArrayList<Message> messages = new ArrayList<Message>();
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
|
||||
for (MessageReference ref : refs) {
|
||||
Message m = ref.restoreToLocalMessage(this);
|
||||
|
@ -12,6 +12,7 @@ import com.fsck.k9.helper.power.TracingPowerManager;
|
||||
import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PollService extends CoreService {
|
||||
private static String START_SERVICE = "com.fsck.k9.service.PollService.startService";
|
||||
@ -77,7 +78,7 @@ public class PollService extends CoreService {
|
||||
}
|
||||
|
||||
class Listener extends MessagingListener {
|
||||
HashMap<String, Integer> accountsChecked = new HashMap<String, Integer>();
|
||||
Map<String, Integer> accountsChecked = new HashMap<String, Integer>();
|
||||
private TracingWakeLock wakeLock = null;
|
||||
private int startId = -1;
|
||||
|
||||
|
@ -11,6 +11,8 @@ import com.fsck.k9.K9;
|
||||
import com.fsck.k9.remotecontrol.K9RemoteControl;
|
||||
import com.fsck.k9.Preferences;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.fsck.k9.remotecontrol.K9RemoteControl.*;
|
||||
|
||||
public class RemoteControlReceiver extends CoreReceiver {
|
||||
@ -25,12 +27,12 @@ public class RemoteControlReceiver extends CoreReceiver {
|
||||
} else if (K9RemoteControl.K9_REQUEST_ACCOUNTS.equals(intent.getAction())) {
|
||||
try {
|
||||
Preferences preferences = Preferences.getPreferences(context);
|
||||
Account[] accounts = preferences.getAccounts();
|
||||
String[] uuids = new String[accounts.length];
|
||||
String[] descriptions = new String[accounts.length];
|
||||
for (int i = 0; i < accounts.length; i++) {
|
||||
List<Account> accounts = preferences.getAccounts();
|
||||
String[] uuids = new String[accounts.size()];
|
||||
String[] descriptions = new String[accounts.size()];
|
||||
for (int i = 0; i < accounts.size(); i++) {
|
||||
//warning: account may not be isAvailable()
|
||||
Account account = accounts[i];
|
||||
Account account = accounts.get(i);
|
||||
|
||||
uuids[i] = account.getUuid();
|
||||
descriptions[i] = account.getDescription();
|
||||
|
@ -17,6 +17,8 @@ import android.content.SharedPreferences.Editor;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RemoteControlService extends CoreService {
|
||||
private final static String RESCHEDULE_ACTION = "com.fsck.k9.service.RemoteControlService.RESCHEDULE_ACTION";
|
||||
private final static String PUSH_RESTART_ACTION = "com.fsck.k9.service.RemoteControlService.PUSH_RESTART_ACTION";
|
||||
@ -65,7 +67,7 @@ public class RemoteControlService extends CoreService {
|
||||
Log.i(K9.LOG_TAG, "RemoteControlService changing settings for account with UUID " + uuid);
|
||||
}
|
||||
}
|
||||
Account[] accounts = preferences.getAccounts();
|
||||
List<Account> accounts = preferences.getAccounts();
|
||||
for (Account account : accounts) {
|
||||
//warning: account may not be isAvailable()
|
||||
if (allAccounts || account.getUuid().equals(uuid)) {
|
||||
|
@ -40,7 +40,7 @@ import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Part;
|
||||
import com.fsck.k9.mail.internet.MimeHeader;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBodyPart;
|
||||
import com.fsck.k9.mail.store.local.LocalAttachmentBodyPart;
|
||||
import com.fsck.k9.provider.AttachmentProvider;
|
||||
|
||||
public class AttachmentView extends FrameLayout implements OnClickListener, OnLongClickListener {
|
||||
|
@ -5,6 +5,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
@ -279,12 +280,7 @@ public class MessageOpenPgpView extends LinearLayout {
|
||||
String accName = OpenPgpApiHelper.buildAccountName(identity);
|
||||
intent.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, accName);
|
||||
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new ByteArrayInputStream(mData.getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(K9.LOG_TAG, "UnsupportedEncodingException.", e);
|
||||
}
|
||||
InputStream is = new ByteArrayInputStream(mData.getBytes(Charset.forName("UTF-8")));
|
||||
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
DecryptVerifyCallback callback = new DecryptVerifyCallback(os, REQUEST_CODE_DECRYPT_VERIFY);
|
||||
|
@ -28,6 +28,7 @@ import android.webkit.WebView;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link ScrollView} that will never lock scrolling in a particular direction.
|
||||
@ -59,7 +60,7 @@ public class NonLockingScrollView extends ScrollView {
|
||||
/**
|
||||
* The list of children who should always receive touch events, and not have them intercepted.
|
||||
*/
|
||||
private final ArrayList<View> mChildrenNeedingAllTouches = new ArrayList<View>();
|
||||
private final List<View> mChildrenNeedingAllTouches = new ArrayList<View>();
|
||||
|
||||
private boolean mSkipWebViewScroll = true;
|
||||
|
||||
@ -122,7 +123,7 @@ public class NonLockingScrollView extends ScrollView {
|
||||
}
|
||||
|
||||
private final Rect sHitFrame = new Rect();
|
||||
private boolean isEventOverChild(MotionEvent ev, ArrayList<View> children) {
|
||||
private boolean isEventOverChild(MotionEvent ev, List<View> children) {
|
||||
final int actionIndex = ev.getActionIndex();
|
||||
final float x = ev.getX(actionIndex) + getScrollX();
|
||||
final float y = ev.getY(actionIndex) + getScrollY();
|
||||
|
@ -47,6 +47,7 @@ import com.fsck.k9.fragment.MessageViewFragment;
|
||||
import com.fsck.k9.helper.ClipboardManager;
|
||||
import com.fsck.k9.helper.Contacts;
|
||||
import com.fsck.k9.helper.HtmlConverter;
|
||||
import com.fsck.k9.helper.UrlEncodingHelper;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.mail.Flag;
|
||||
@ -55,8 +56,8 @@ import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Multipart;
|
||||
import com.fsck.k9.mail.Part;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.store.LocalStore;
|
||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
||||
import com.fsck.k9.mail.store.local.LocalAttachmentBodyPart;
|
||||
import com.fsck.k9.mail.store.local.LocalMessage;
|
||||
import com.fsck.k9.provider.AttachmentProvider.AttachmentProviderColumns;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
@ -626,7 +627,7 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
|
||||
for (int i = 0; i < mp.getCount(); i++) {
|
||||
renderAttachments(mp.getBodyPart(i), depth + 1, message, account, controller, listener);
|
||||
}
|
||||
} else if (part instanceof LocalStore.LocalAttachmentBodyPart) {
|
||||
} else if (part instanceof LocalAttachmentBodyPart) {
|
||||
AttachmentView view = (AttachmentView)mInflater.inflate(R.layout.message_view_attachment, null);
|
||||
view.setCallback(attachmentCallback);
|
||||
|
||||
@ -790,7 +791,7 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
|
||||
// Try to get the filename from the URL
|
||||
int start = path.lastIndexOf("/");
|
||||
if (start != -1 && start + 1 < path.length()) {
|
||||
filename = URLDecoder.decode(path.substring(start + 1), "UTF-8");
|
||||
filename = UrlEncodingHelper.decodeUtf8(path.substring(start + 1));
|
||||
} else {
|
||||
// Use a dummy filename if necessary
|
||||
filename = "saved_image";
|
||||
|
0
tests-on-jvm/gradlew
vendored
Normal file → Executable file
0
tests-on-jvm/gradlew
vendored
Normal file → Executable file
@ -44,7 +44,7 @@ class TestingTextBodyBuilder extends TextBodyBuilder {
|
||||
// HtmlConverter depends on Android.
|
||||
// So we use dummy method for tests.
|
||||
@Override
|
||||
public String textToHtmlFragment(String text) {
|
||||
protected String textToHtmlFragment(String text) {
|
||||
return "<html>" + text + "</html>";
|
||||
}
|
||||
}
|
||||
|
236
tests/src/com/fsck/k9/mail/internet/MimeMessageParseTest.java
Normal file
236
tests/src/com/fsck/k9/mail/internet/MimeMessageParseTest.java
Normal file
@ -0,0 +1,236 @@
|
||||
package com.fsck.k9.mail.internet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.mail.Body;
|
||||
import com.fsck.k9.mail.BodyPart;
|
||||
import com.fsck.k9.mail.Message.RecipientType;
|
||||
import com.fsck.k9.mail.Multipart;
|
||||
import com.fsck.k9.mail.internet.MimeMessage;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
public class MimeMessageParseTest extends AndroidTestCase {
|
||||
|
||||
static {
|
||||
BinaryTempFileBody.setTempDirectory(new File(System.getProperty("java.io.tmpdir")));
|
||||
}
|
||||
|
||||
private static ByteArrayInputStream toStream(String rawMailData) throws Exception {
|
||||
return new ByteArrayInputStream(rawMailData.getBytes("ISO-8859-1"));
|
||||
}
|
||||
|
||||
private static MimeMessage parseWithoutRecurse(InputStream data) throws Exception {
|
||||
return new MimeMessage(data, false);
|
||||
}
|
||||
|
||||
private static MimeMessage parseWithRecurse(InputStream data) throws Exception {
|
||||
return new MimeMessage(data, true);
|
||||
}
|
||||
|
||||
private static void checkAddresses(Address[] actual, String... expected) {
|
||||
for (int i = 0; i < actual.length; i++) {
|
||||
assertEquals(actual[i].toEncodedString(), expected[i]);
|
||||
}
|
||||
assertEquals(expected.length, actual.length);
|
||||
}
|
||||
|
||||
private static String streamToString(InputStream stream) throws Exception {
|
||||
return IOUtils.toString(stream, "ISO-8859-1");
|
||||
}
|
||||
|
||||
private static List<Body> getLeafParts(Body body) {
|
||||
if (body instanceof Multipart) {
|
||||
List<Body> ret = new ArrayList<Body>();
|
||||
for (BodyPart child : ((Multipart) body).getBodyParts()) {
|
||||
ret.addAll(getLeafParts(child.getBody()));
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
return Collections.singletonList(body);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkLeafParts(MimeMessage msg, String... expectedParts) throws Exception {
|
||||
List<String> actual = new ArrayList<String>();
|
||||
for (Body leaf : getLeafParts(msg.getBody())) {
|
||||
actual.add(streamToString(leaf.getInputStream()));
|
||||
}
|
||||
assertEquals(Arrays.asList(expectedParts), actual);
|
||||
}
|
||||
|
||||
public static void testSinglePart7BitNoRecurse() throws Exception {
|
||||
MimeMessage msg = parseWithoutRecurse(toStream(
|
||||
"From: <adam@example.org>\r\n" +
|
||||
"To: <eva@example.org>\r\n" +
|
||||
"Subject: Testmail\r\n" +
|
||||
"MIME-Version: 1.0\r\n" +
|
||||
"Content-type: text/plain\r\n" +
|
||||
"Content-Transfer-Encoding: 7bit\r\n" +
|
||||
"\r\n" +
|
||||
"this is some test text."));
|
||||
|
||||
checkAddresses(msg.getFrom(), "adam@example.org");
|
||||
checkAddresses(msg.getRecipients(RecipientType.TO), "eva@example.org");
|
||||
assertEquals("Testmail", msg.getSubject());
|
||||
assertEquals("text/plain", msg.getContentType());
|
||||
assertEquals("this is some test text.", streamToString(msg.getBody().getInputStream()));
|
||||
}
|
||||
|
||||
public static void testSinglePart8BitRecurse() throws Exception {
|
||||
MimeMessage msg = parseWithRecurse(toStream(
|
||||
"From: <adam@example.org>\r\n" +
|
||||
"To: <eva@example.org>\r\n" +
|
||||
"Subject: Testmail\r\n" +
|
||||
"MIME-Version: 1.0\r\n" +
|
||||
"Content-type: text/plain; encoding=ISO-8859-1\r\n" +
|
||||
"Content-Transfer-Encoding: 8bit\r\n" +
|
||||
"\r\n" +
|
||||
"gefährliche Umlaute"));
|
||||
|
||||
checkAddresses(msg.getFrom(), "adam@example.org");
|
||||
checkAddresses(msg.getRecipients(RecipientType.TO), "eva@example.org");
|
||||
assertEquals("Testmail", msg.getSubject());
|
||||
assertEquals("text/plain; encoding=ISO-8859-1", msg.getContentType());
|
||||
assertEquals("gefährliche Umlaute", streamToString(msg.getBody().getInputStream()));
|
||||
}
|
||||
|
||||
public static void testSinglePartBase64NoRecurse() throws Exception {
|
||||
MimeMessage msg = parseWithoutRecurse(toStream(
|
||||
"From: <adam@example.org>\r\n" +
|
||||
"To: <eva@example.org>\r\n" +
|
||||
"Subject: Testmail\r\n" +
|
||||
"MIME-Version: 1.0\r\n" +
|
||||
"Content-type: text/plain\r\n" +
|
||||
"Content-Transfer-Encoding: base64\r\n" +
|
||||
"\r\n" +
|
||||
"dGhpcyBpcyBzb21lIG1vcmUgdGVzdCB0ZXh0Lg==\r\n"));
|
||||
|
||||
checkAddresses(msg.getFrom(), "adam@example.org");
|
||||
checkAddresses(msg.getRecipients(RecipientType.TO), "eva@example.org");
|
||||
assertEquals("Testmail", msg.getSubject());
|
||||
assertEquals("text/plain", msg.getContentType());
|
||||
assertEquals("this is some more test text.", streamToString(msg.getBody().getInputStream()));
|
||||
}
|
||||
|
||||
public static void testMultipartSingleLayerNoRecurse() throws Exception {
|
||||
MimeMessage msg = parseWithoutRecurse(toStream(
|
||||
"From: <x@example.org>\r\n" +
|
||||
"To: <y@example.org>\r\n" +
|
||||
"Subject: Testmail 2\r\n" +
|
||||
"MIME-Version: 1.0\n" +
|
||||
"Content-Type: multipart/mixed; boundary=frontier\n" +
|
||||
"\n" +
|
||||
"This is a message with multiple parts in MIME format.\n" +
|
||||
"--frontier\n" +
|
||||
"Content-Type: text/plain\n" +
|
||||
"\n" +
|
||||
"This is the body of the message.\n" +
|
||||
"--frontier\n" +
|
||||
"Content-Type: application/octet-stream\n" +
|
||||
"Content-Transfer-Encoding: base64\n" +
|
||||
"\n" +
|
||||
"PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg\n" +
|
||||
"Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg=\n" +
|
||||
"--frontier--"));
|
||||
|
||||
checkAddresses(msg.getFrom(), "x@example.org");
|
||||
checkAddresses(msg.getRecipients(RecipientType.TO), "y@example.org");
|
||||
assertEquals("Testmail 2", msg.getSubject());
|
||||
assertEquals("multipart/mixed; boundary=frontier", msg.getContentType());
|
||||
checkLeafParts(msg,
|
||||
"This is the body of the message.",
|
||||
"<html>\n" +
|
||||
" <head>\n" +
|
||||
" </head>\n" +
|
||||
" <body>\n" +
|
||||
" <p>This is the body of the message.</p>\n" +
|
||||
" </body>\n" +
|
||||
"</html>\n" +
|
||||
"");
|
||||
}
|
||||
|
||||
public static void testMultipartSingleLayerRecurse() throws Exception {
|
||||
MimeMessage msg = parseWithRecurse(toStream(
|
||||
"From: <x@example.org>\r\n" +
|
||||
"To: <y@example.org>\r\n" +
|
||||
"Subject: Testmail 2\r\n" +
|
||||
"MIME-Version: 1.0\n" +
|
||||
"Content-Type: multipart/mixed; boundary=frontier\n" +
|
||||
"\n" +
|
||||
"This is a message with multiple parts in MIME format.\n" +
|
||||
"--frontier\n" +
|
||||
"Content-Type: text/plain\n" +
|
||||
"\n" +
|
||||
"This is the body of the message.\n" +
|
||||
"--frontier\n" +
|
||||
"Content-Type: application/octet-stream\n" +
|
||||
"Content-Transfer-Encoding: base64\n" +
|
||||
"\n" +
|
||||
"PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg\n" +
|
||||
"Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg=\n" +
|
||||
"--frontier--"));
|
||||
|
||||
checkAddresses(msg.getFrom(), "x@example.org");
|
||||
checkAddresses(msg.getRecipients(RecipientType.TO), "y@example.org");
|
||||
assertEquals("Testmail 2", msg.getSubject());
|
||||
assertEquals("multipart/mixed; boundary=frontier", msg.getContentType());
|
||||
checkLeafParts(msg,
|
||||
"This is the body of the message.",
|
||||
"<html>\n" +
|
||||
" <head>\n" +
|
||||
" </head>\n" +
|
||||
" <body>\n" +
|
||||
" <p>This is the body of the message.</p>\n" +
|
||||
" </body>\n" +
|
||||
"</html>\n" +
|
||||
"");
|
||||
}
|
||||
|
||||
public static void testMultipartTwoLayersRecurse() throws Exception {
|
||||
MimeMessage msg = parseWithRecurse(toStream(
|
||||
"From: <x@example.org>\r\n" +
|
||||
"To: <y@example.org>\r\n" +
|
||||
"Subject: Testmail 2\r\n" +
|
||||
"MIME-Version: 1.0\n" +
|
||||
"Content-Type: multipart/mixed; boundary=1\n" +
|
||||
"\n" +
|
||||
"This is a message with multiple parts in MIME format.\n" +
|
||||
"--1\n" +
|
||||
"Content-Type: text/plain\n" +
|
||||
"\n" +
|
||||
"some text in the first part\n" +
|
||||
"--1\n" +
|
||||
"Content-Type: multipart/alternative; boundary=2\n" +
|
||||
"\n" +
|
||||
"--2\n" +
|
||||
"Content-Type: text/plain\n" +
|
||||
"\n" +
|
||||
"alternative 1\n" +
|
||||
"--2\n" +
|
||||
"Content-Type: text/plain\n" +
|
||||
"\n" +
|
||||
"alternative 2\n" +
|
||||
"--2--\n" +
|
||||
"--1--"));
|
||||
|
||||
checkAddresses(msg.getFrom(), "x@example.org");
|
||||
checkAddresses(msg.getRecipients(RecipientType.TO), "y@example.org");
|
||||
assertEquals("Testmail 2", msg.getSubject());
|
||||
assertEquals("multipart/mixed; boundary=1", msg.getContentType());
|
||||
checkLeafParts(msg,
|
||||
"some text in the first part",
|
||||
"alternative 1",
|
||||
"alternative 2");
|
||||
}
|
||||
|
||||
}
|
@ -100,7 +100,7 @@ public class ViewablesTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
public void testTextPlusRfc822Message() throws MessagingException {
|
||||
K9ActivityCommon.setLanguage(getContext(), "en");
|
||||
K9ActivityCommon.setLanguage(getContext(), "en");
|
||||
Locale.setDefault(Locale.US);
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("GMT+01:00"));
|
||||
|
||||
|
@ -96,70 +96,82 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
|
||||
+ "Dcf5/G8bZe2DnavBQfML1wI5d7NUWE8CWb95SsIvFXI0qZE0oIR+axBVl9u97uaO\n"
|
||||
+ "-----END CERTIFICATE-----\n";
|
||||
|
||||
private static final String STARFIELD_CERT =
|
||||
private static final String LINUX_COM_FIRST_PARENT_CERT =
|
||||
"-----BEGIN CERTIFICATE-----\n" +
|
||||
"MIIFBzCCA++gAwIBAgICAgEwDQYJKoZIhvcNAQEFBQAwaDELMAkGA1UEBhMCVVMx\n" +
|
||||
"JTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsT\n" +
|
||||
"KVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2\n" +
|
||||
"MTExNjAxMTU0MFoXDTI2MTExNjAxMTU0MFowgdwxCzAJBgNVBAYTAlVTMRAwDgYD\n" +
|
||||
"VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy\n" +
|
||||
"ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTkwNwYDVQQLEzBodHRwOi8vY2VydGlm\n" +
|
||||
"aWNhdGVzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkxMTAvBgNVBAMTKFN0\n" +
|
||||
"YXJmaWVsZCBTZWN1cmUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxETAPBgNVBAUT\n" +
|
||||
"CDEwNjg4NDM1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4qddo+1m\n" +
|
||||
"72ovKzYf3Y3TBQKgyg9eGa44cs8W2lRKy0gK9KFzEWWFQ8lbFwyaK74PmFF6YCkN\n" +
|
||||
"bN7i6OUVTVb/kNGnpgQ/YAdKym+lEOez+FyxvCsq3AF59R019Xoog/KTc4KJrGBt\n" +
|
||||
"y8JIwh3UBkQXPKwBR6s+cIQJC7ggCEAgh6FjGso+g9I3s5iNMj83v6G3W1/eXDOS\n" +
|
||||
"zz4HzrlIS+LwVVAv+HBCidGTlopj2WYN5lhuuW2QvcrchGbyOY5bplhVc8tibBvX\n" +
|
||||
"IBY7LFn1y8hWMkpQJ7pV06gBy3KpdIsMrTrlFbYq32X43or174Q7+edUZQuAvUdF\n" +
|
||||
"pfBE2FM7voDxLwIDAQABo4IBRDCCAUAwHQYDVR0OBBYEFElLUifRG7zyoSFqYntR\n" +
|
||||
"QnqK19VWMB8GA1UdIwQYMBaAFL9ft9HO3R+G9FtVrNzXEMIOqYjnMBIGA1UdEwEB\n" +
|
||||
"/wQIMAYBAf8CAQAwOQYIKwYBBQUHAQEELTArMCkGCCsGAQUFBzABhh1odHRwOi8v\n" +
|
||||
"b2NzcC5zdGFyZmllbGR0ZWNoLmNvbTBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8v\n" +
|
||||
"Y2VydGlmaWNhdGVzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkvc2Zyb290\n" +
|
||||
"LmNybDBRBgNVHSAESjBIMEYGBFUdIAAwPjA8BggrBgEFBQcCARYwaHR0cDovL2Nl\n" +
|
||||
"cnRpZmljYXRlcy5zdGFyZmllbGR0ZWNoLmNvbS9yZXBvc2l0b3J5MA4GA1UdDwEB\n" +
|
||||
"/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAhlK6sx+mXmuQpmQq/EWyrp8+s2Kv\n" +
|
||||
"2x9nxL3KoS/HnA0hV9D4NiHOOiU+eHaz2d283vtshF8Mow0S6xE7cV+AHvEfbQ5f\n" +
|
||||
"wezUpfdlux9MlQETsmqcC+sfnbHn7RkNvIV88xe9WWOupxoFzUfjLZZiUTIKCGhL\n" +
|
||||
"Indf90XcYd70yysiKUQl0p8Ld3qhJnxK1w/C0Ty6DqeVmlsFChD5VV/Bl4t0zF4o\n" +
|
||||
"aRN+0AqNnQ9gVHrEjBs1D3R6cLKCzx214orbKsayUWm/EheSYBeqPVsJ+IdlHaek\n" +
|
||||
"KOUiAgOCRJo0Y577KM/ozS4OUiDtSss4fJ2ubnnXlSyokfOGASGRS7VApA==\n" +
|
||||
"MIIGNDCCBBygAwIBAgIBGzANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW\n" +
|
||||
"MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg\n" +
|
||||
"Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh\n" +
|
||||
"dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NzA5WhcNMTcxMDI0MjA1NzA5WjCB\n" +
|
||||
"jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT\n" +
|
||||
"IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0\n" +
|
||||
"YXJ0Q29tIENsYXNzIDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB\n" +
|
||||
"IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4k85L6GMmoWtCA4IPlfyiAEh\n" +
|
||||
"G5SpbOK426oZGEY6UqH1D/RujOqWjJaHeRNAUS8i8gyLhw9l33F0NENVsTUJm9m8\n" +
|
||||
"H/rrQtCXQHK3Q5Y9upadXVACHJuRjZzArNe7LxfXyz6CnXPrB0KSss1ks3RVG7RL\n" +
|
||||
"hiEs93iHMuAW5Nq9TJXqpAp+tgoNLorPVavD5d1Bik7mb2VsskDPF125w2oLJxGE\n" +
|
||||
"d2H2wnztwI14FBiZgZl1Y7foU9O6YekO+qIw80aiuckfbIBaQKwn7UhHM7BUxkYa\n" +
|
||||
"8zVhwQIpkFR+ZE3EMFICgtffziFuGJHXuKuMJxe18KMBL47SLoc6PbQpZ4rEAwID\n" +
|
||||
"AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD\n" +
|
||||
"VR0OBBYEFBHbI0X9VMxqcW+EigPXvvcBLyaGMB8GA1UdIwQYMBaAFE4L7xqkQFul\n" +
|
||||
"F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov\n" +
|
||||
"L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0\n" +
|
||||
"YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3\n" +
|
||||
"dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0\n" +
|
||||
"c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu\n" +
|
||||
"BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0\n" +
|
||||
"BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl\n" +
|
||||
"LnBkZjANBgkqhkiG9w0BAQsFAAOCAgEAbQjxXHkqUPtUY+u8NEFcuKMDITfjvGkl\n" +
|
||||
"LgrTuBW63grW+2AuDAZRo/066eNHs6QV4i5e4ujwPSR2dgggY7mOIIBmiDm2QRjF\n" +
|
||||
"5CROq6zDlIdqlsFZICkuONDNFpFjaPtZRTmuK1n6gywQgCNSIrbzjPcwR/jL/wow\n" +
|
||||
"bfwC9yGme1EeZRqvWy/HzFWacs7UMmWlRk6DTmpfPOPMJo5AxyTZCiCYQQeksV7x\n" +
|
||||
"UAeY0kWa+y/FV+eerOPUl6yy4jRHTk7tCySxrciZwYbd6YNLmeIQoUAdRC3CH3nT\n" +
|
||||
"B2/JYxltcgyGHMiPU3TtafZgLs8fvncv+wIF1YAF/OGqg8qmzoJ3ghM4upGdTMIu\n" +
|
||||
"8vADdmuLC/+dnbzknxX6QEGlWA8zojLUxVhGNfIFoizu/V/DyvSvYuxzzIkPECK5\n" +
|
||||
"gDoMoBTTMI/wnxXwulNPtfgF7/5AtDhA4GNAfB2SddxiNQAF7XkUHtMZ9ff3W6Xk\n" +
|
||||
"FldOG+NlLFqsDBG/KLckyFK36gq+FqNFCbmtmtXBGB5L1fDIeYzcMKG6hFQxhHS0\n" +
|
||||
"oqpdHhp2nWBfLlOnTNqIZNJzOH37OJE6Olk45LNFJtSrqIAZyCCfM6bQgoQvZuIa\n" +
|
||||
"xs9SIp+63ZMk9TxEaQj/KteaOyfaPXI9778U7JElMTz3Bls62mslV2I1C/A73Zyq\n" +
|
||||
"JZWQZ8NU4ds=\n" +
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
private static final String LINUX_COM_CERT =
|
||||
"-----BEGIN CERTIFICATE-----\n" +
|
||||
"MIIFfDCCBGSgAwIBAgIHJ7DOOMo+MDANBgkqhkiG9w0BAQUFADCB3DELMAkGA1UE\n" +
|
||||
"BhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAj\n" +
|
||||
"BgNVBAoTHFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOTA3BgNVBAsTMGh0\n" +
|
||||
"dHA6Ly9jZXJ0aWZpY2F0ZXMuc3RhcmZpZWxkdGVjaC5jb20vcmVwb3NpdG9yeTEx\n" +
|
||||
"MC8GA1UEAxMoU3RhcmZpZWxkIFNlY3VyZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0\n" +
|
||||
"eTERMA8GA1UEBRMIMTA2ODg0MzUwHhcNMTExMDA1MDI1MTQyWhcNMTQxMDA1MDI1\n" +
|
||||
"MTQyWjBPMRQwEgYDVQQKFAsqLmxpbnV4LmNvbTEhMB8GA1UECxMYRG9tYWluIENv\n" +
|
||||
"bnRyb2wgVmFsaWRhdGVkMRQwEgYDVQQDFAsqLmxpbnV4LmNvbTCCASIwDQYJKoZI\n" +
|
||||
"hvcNAQEBBQADggEPADCCAQoCggEBANoZR/TDp2/8LtA8k9Li55I665ssC7rHX+Wk\n" +
|
||||
"oiGa6xBeCKTvNy9mgaUVzHwrOQlwJ2GbxFI+X0e3W2sWXUDTSxESZSEW2VZnjEn2\n" +
|
||||
"600Qm8XMhZPvqztLRweHH8IuBNNYZHnW4Z2L4DS/Mi03EmjKZt2g3heGQbrv74m4\n" +
|
||||
"v9/g6Jgr5ZOIwES6LUJchSWV2zcL8VYunpxnAtbi2hq1YfA9oYU82ngP40Ds7HEB\n" +
|
||||
"9pUlzcWu9gcasWGzTvbVBZ4nA29pz5zWn1LHYfSYVSmXKU/ggfZb2nXd5/NkbWQX\n" +
|
||||
"7B2SNH9/OVrHtFZldzD1+ddfCt1DQjXfGv7QqpAVsFTdKspPDLMCAwEAAaOCAc0w\n" +
|
||||
"ggHJMA8GA1UdEwEB/wQFMAMBAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF\n" +
|
||||
"BwMCMA4GA1UdDwEB/wQEAwIFoDA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vY3Js\n" +
|
||||
"LnN0YXJmaWVsZHRlY2guY29tL3NmczEtMjAuY3JsMFkGA1UdIARSMFAwTgYLYIZI\n" +
|
||||
"AYb9bgEHFwEwPzA9BggrBgEFBQcCARYxaHR0cDovL2NlcnRpZmljYXRlcy5zdGFy\n" +
|
||||
"ZmllbGR0ZWNoLmNvbS9yZXBvc2l0b3J5LzCBjQYIKwYBBQUHAQEEgYAwfjAqBggr\n" +
|
||||
"BgEFBQcwAYYeaHR0cDovL29jc3Auc3RhcmZpZWxkdGVjaC5jb20vMFAGCCsGAQUF\n" +
|
||||
"BzAChkRodHRwOi8vY2VydGlmaWNhdGVzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9z\n" +
|
||||
"aXRvcnkvc2ZfaW50ZXJtZWRpYXRlLmNydDAfBgNVHSMEGDAWgBRJS1In0Ru88qEh\n" +
|
||||
"amJ7UUJ6itfVVjAhBgNVHREEGjAYggsqLmxpbnV4LmNvbYIJbGludXguY29tMB0G\n" +
|
||||
"A1UdDgQWBBQ44sIiZfPIl4PY51fh2TCZkqtToTANBgkqhkiG9w0BAQUFAAOCAQEA\n" +
|
||||
"HFMuDtEZ+hIrIp4hnRJXUiTsc4Vaycxd5X/axDzUx+ooT3y2jBw0rcNnFhgD1T3u\n" +
|
||||
"9zKiOLGXidvy2G/ppy/ymE+gcNqcEzfV1pKggNqStCwpEX1K8GBD46mX5qJ1RxI+\n" +
|
||||
"QoHo/FZe7Vt+dQjHHdGWh27iVWadpBo/FJnHOsTaHewKL8+Aho0M84nxnUolYxzC\n" +
|
||||
"9H3ViEz+mfMISLzvWicxVU71aJ4yI9JmaL1ddRppBovZHOeWshizcMVtFwcza1S0\n" +
|
||||
"ZfajonXj48ZkXMXGWuomWxE2dGro6ZW6DdyIjTpZHCJuIvGC10J3mHIR5XaTj6mv\n" +
|
||||
"zkVBz5DhpshQe97x6OGLOA==\n" +
|
||||
"-----END CERTIFICATE-----\n";
|
||||
"-----BEGIN CERTIFICATE-----\n" +
|
||||
"MIIGhjCCBW6gAwIBAgIDAmiWMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJJ\n" +
|
||||
"TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0\n" +
|
||||
"YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg\n" +
|
||||
"MiBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTQwODIxMjEwMDI4\n" +
|
||||
"WhcNMTYwODIxMDY0NDE0WjCBlDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlm\n" +
|
||||
"b3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xHTAbBgNVBAoTFFRoZSBMaW51\n" +
|
||||
"eCBGb3VuZGF0aW9uMRQwEgYDVQQDFAsqLmxpbnV4LmNvbTEjMCEGCSqGSIb3DQEJ\n" +
|
||||
"ARYUaG9zdG1hc3RlckBsaW51eC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\n" +
|
||||
"ggEKAoIBAQCjvFjOigXyqkSiVv0vz1CSDg08iilLnj8gRFRoRMA6fFWhQTp4QGLV\n" +
|
||||
"1li5VMEQdZ/vyqTWjmB+FFkuTsBjFDg6gG3yw6DQBGyyM06A1dT9YKUa7LqxOxQr\n" +
|
||||
"KhNOacPS/pAupAZ5jOO7fcZwIcpKcKEjjhHn7GXEVvb+K996TMA0vDYcp1lgXtil\n" +
|
||||
"7Ij+1GUSA29NrnCZXUun2c5nS7OulRYcgtRyZBa13zfyaVJtEIl14ClP9gsLa/5u\n" +
|
||||
"eXzZD71Jj48ZNbiKRThiUZ5FkEnljjSQa25Hr5g9DXY2JvI1r8OJOCUR8jPiRyNs\n" +
|
||||
"Kgc1ZG0fibm9VoHjokUZ2aQxyQJP/C1TAgMBAAGjggLlMIIC4TAJBgNVHRMEAjAA\n" +
|
||||
"MAsGA1UdDwQEAwIDqDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwHQYD\n" +
|
||||
"VR0OBBYEFI0nMnIXZOz02MlXPh2g9aHesvPPMB8GA1UdIwQYMBaAFBHbI0X9VMxq\n" +
|
||||
"cW+EigPXvvcBLyaGMCEGA1UdEQQaMBiCCyoubGludXguY29tgglsaW51eC5jb20w\n" +
|
||||
"ggFWBgNVHSAEggFNMIIBSTAIBgZngQwBAgIwggE7BgsrBgEEAYG1NwECAzCCASow\n" +
|
||||
"LgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYw\n" +
|
||||
"gfcGCCsGAQUFBwICMIHqMCcWIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9y\n" +
|
||||
"aXR5MAMCAQEagb5UaGlzIGNlcnRpZmljYXRlIHdhcyBpc3N1ZWQgYWNjb3JkaW5n\n" +
|
||||
"IHRvIHRoZSBDbGFzcyAyIFZhbGlkYXRpb24gcmVxdWlyZW1lbnRzIG9mIHRoZSBT\n" +
|
||||
"dGFydENvbSBDQSBwb2xpY3ksIHJlbGlhbmNlIG9ubHkgZm9yIHRoZSBpbnRlbmRl\n" +
|
||||
"ZCBwdXJwb3NlIGluIGNvbXBsaWFuY2Ugb2YgdGhlIHJlbHlpbmcgcGFydHkgb2Js\n" +
|
||||
"aWdhdGlvbnMuMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwuc3RhcnRzc2wu\n" +
|
||||
"Y29tL2NydDItY3JsLmNybDCBjgYIKwYBBQUHAQEEgYEwfzA5BggrBgEFBQcwAYYt\n" +
|
||||
"aHR0cDovL29jc3Auc3RhcnRzc2wuY29tL3N1Yi9jbGFzczIvc2VydmVyL2NhMEIG\n" +
|
||||
"CCsGAQUFBzAChjZodHRwOi8vYWlhLnN0YXJ0c3NsLmNvbS9jZXJ0cy9zdWIuY2xh\n" +
|
||||
"c3MyLnNlcnZlci5jYS5jcnQwIwYDVR0SBBwwGoYYaHR0cDovL3d3dy5zdGFydHNz\n" +
|
||||
"bC5jb20vMA0GCSqGSIb3DQEBCwUAA4IBAQBVkvlwVLfnTNZh1c8j+PQ1t2n6x1dh\n" +
|
||||
"tQtZiAYWKvZwi+XqLwU8q2zMxKrTDuqyEVyfCtWCiC1Vkpz72pcyXz2dKu7F7ZVL\n" +
|
||||
"86uVHcc1jAGmL59UCXz8LFbfAMcoVQW1f2WtNwsa/WGnPUes3jFSec+shB+XCpvE\n" +
|
||||
"WU6mfcZD5TyvbC79Kn5e3Iq+B4DaXhU/BXASRbORgYd8C+dqj++w0PAcMrmjn3D6\n" +
|
||||
"EmL1ofqpQ8wCJd5C1b5Fr4RbbYpK8v8AASRcp2Qj9WJjyV882FvXOOFj5V2Jjcnh\n" +
|
||||
"G0h67ElS/klu9rPaZ+vr3iIB56wvk08O2Wq1IND3sN+Ke3UsvuPqDxAv\n" +
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
private File mKeyStoreFile;
|
||||
private LocalKeyStore mKeyStore;
|
||||
@ -167,7 +179,7 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
|
||||
private X509Certificate mCert2;
|
||||
private X509Certificate mCaCert;
|
||||
private X509Certificate mCert3;
|
||||
private X509Certificate mStarfieldCert;
|
||||
private X509Certificate mLinuxComFirstParentCert;
|
||||
private X509Certificate mLinuxComCert;
|
||||
|
||||
|
||||
@ -176,7 +188,7 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
|
||||
mCert2 = loadCert(K9_EXAMPLE_COM_CERT2);
|
||||
mCaCert = loadCert(CA_CERT);
|
||||
mCert3 = loadCert(CERT3);
|
||||
mStarfieldCert = loadCert(STARFIELD_CERT);
|
||||
mLinuxComFirstParentCert = loadCert(LINUX_COM_FIRST_PARENT_CERT);
|
||||
mLinuxComCert = loadCert(LINUX_COM_CERT);
|
||||
}
|
||||
|
||||
@ -267,20 +279,20 @@ public class TrustManagerFactoryTest extends AndroidTestCase {
|
||||
|
||||
public void testGloballyTrustedCertificateChain() throws Exception {
|
||||
X509TrustManager trustManager = TrustManagerFactory.get("www.linux.com", PORT1);
|
||||
X509Certificate[] certificates = new X509Certificate[] { mLinuxComCert, mStarfieldCert };
|
||||
X509Certificate[] certificates = new X509Certificate[] { mLinuxComCert, mLinuxComFirstParentCert};
|
||||
trustManager.checkServerTrusted(certificates, "authType");
|
||||
}
|
||||
|
||||
public void testGloballyTrustedCertificateNotMatchingHost() throws Exception {
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(NOT_MATCHING_HOST, PORT1);
|
||||
assertCertificateRejection(trustManager, new X509Certificate[] { mLinuxComCert, mStarfieldCert });
|
||||
assertCertificateRejection(trustManager, new X509Certificate[] { mLinuxComCert, mLinuxComFirstParentCert});
|
||||
}
|
||||
|
||||
public void testGloballyTrustedCertificateNotMatchingHostOverride() throws Exception {
|
||||
mKeyStore.addCertificate(MATCHING_HOST, PORT1, mLinuxComCert);
|
||||
|
||||
X509TrustManager trustManager = TrustManagerFactory.get(MATCHING_HOST, PORT1);
|
||||
X509Certificate[] certificates = new X509Certificate[] { mLinuxComCert, mStarfieldCert };
|
||||
X509Certificate[] certificates = new X509Certificate[] { mLinuxComCert, mLinuxComFirstParentCert};
|
||||
trustManager.checkServerTrusted(certificates, "authType");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user