Merge remote-tracking branch 'k9mail_pgp_mime/master'

Conflicts:
	src/com/fsck/k9/activity/AccessibleEmailContentActivity.java
This commit is contained in:
cketti 2014-11-12 19:22:53 +01:00
commit 9b61fe0f0e
95 changed files with 5992 additions and 5676 deletions

View File

@ -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) {

View File

@ -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;

View File

@ -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"));
}
}

View File

@ -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)) {

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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;

View File

@ -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();
}

View File

@ -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));

View File

@ -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;

View File

@ -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) {}

View File

@ -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();
}

View File

@ -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);

View File

@ -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;
}

View 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");
}
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -23,7 +23,6 @@ public interface CompositeBody extends Body {
* @throws MessagingException
*
*/
public abstract void setUsing7bitTransport() throws MessagingException;
}

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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)) {

View File

@ -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;
}

View File

@ -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) {

View File

@ -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 + "]");
}
}

View File

@ -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 {

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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")) {

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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));

View File

@ -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(':');

View File

@ -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();
}
}
}

View 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;
}
}

View 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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

File diff suppressed because it is too large Load Diff

View 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;
}
}

File diff suppressed because it is too large Load Diff

View 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

View 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 });
}
}

View 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);
}
}
}

View 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;
}
}

View 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;
}
}

View File

@ -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.*;
@ -33,7 +33,7 @@ public class SmtpTransport extends Transport {
/**
* Decodes a SmtpTransport URI.
*
*
* NOTE: In contrast to ImapStore and Pop3Store, the authType is appended at the end!
*
* <p>Possible forms:</p>
@ -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

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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>();

View File

@ -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;
}

View File

@ -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());

View File

@ -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;

View File

@ -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

View File

@ -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.*;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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());
}
/**

View File

@ -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
*/

View File

@ -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;

View File

@ -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) {

View File

@ -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,13 +38,12 @@ 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);
i.setAction(READ_ALL_ACTION);
return PendingIntent.getService(context, account.getAccountNumber(), i, PendingIntent.FLAG_UPDATE_CURRENT);
}
@ -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);
@ -121,7 +122,7 @@ public class NotificationActionService extends CoreService {
} else {
Log.w(K9.LOG_TAG, "Could not find account for notification action.");
}
return START_NOT_STICKY;
}
}

View File

@ -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;

View File

@ -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();

View File

@ -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)) {

View File

@ -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 {

View File

@ -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);

View File

@ -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();

View File

@ -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
View File

View 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>";
}
}

View 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");
}
}

View File

@ -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"));

View File

@ -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");
}