1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-17 06:55:03 -05:00

Merge commit '4.121' into issue-162-new

Conflicts:
	src/com/fsck/k9/mail/store/LocalStore.java
This commit is contained in:
ashley willis 2012-09-11 01:50:46 -05:00
commit f57ee1181f
27 changed files with 458 additions and 404 deletions

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="15019"
android:versionName="4.120" package="com.fsck.k9"
android:versionCode="15020"
android:versionName="4.121" package="com.fsck.k9"
>
<uses-sdk
android:minSdkVersion="7"

View File

@ -502,6 +502,7 @@
<item>dark</item>
</string-array>
<!-- Note: If you change this make sure the code in Prefs.java is still working -->
<string-array name="background_ops_entries">
<item>@string/background_ops_enabled</item>
<item>@string/background_ops_auto_sync</item>

View File

@ -850,6 +850,7 @@ http://k9mail.googlecode.com/
<string name="background_ops_always">Always</string>
<string name="background_ops_enabled">When \'Background data\' is checked</string>
<string name="background_ops_auto_sync">When \'Background data\' &amp; \'Auto-sync\' are checked</string>
<string name="background_ops_auto_sync_only">When \'Auto-sync\' is checked</string>
<string name="no_message_seletected_toast">No message selected</string>

View File

@ -19,7 +19,6 @@ import com.fsck.k9.mail.store.StorageManager;
import com.fsck.k9.mail.store.StorageManager.StorageProvider;
import com.fsck.k9.view.ColorChip;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;

View File

@ -10,7 +10,6 @@ import java.util.Map;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Config;
import android.util.Log;
import com.fsck.k9.preferences.Editor;
import com.fsck.k9.preferences.Storage;
@ -155,14 +154,6 @@ public class Preferences {
getPreferences().edit().putString("defaultAccountUuid", account.getUuid()).commit();
}
public void dump() {
if (Config.LOGV) {
for (String key : getPreferences().getAll().keySet()) {
Log.v(K9.LOG_TAG, key + " = " + getPreferences().getAll().get(key));
}
}
}
public SharedPreferences getPreferences() {
return mStorage;
}

View File

@ -17,7 +17,6 @@ import java.util.List;
public class ChooseIdentity extends K9ListActivity {
Account mAccount;
String mUID;
ArrayAdapter<String> adapter;
public static final String EXTRA_ACCOUNT = "com.fsck.k9.ChooseIdentity_account";

View File

@ -11,7 +11,6 @@ import android.os.Handler;
import android.os.PowerManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Config;
import android.util.Log;
import android.util.TypedValue;
import android.view.*;
@ -1099,10 +1098,6 @@ public class FolderList extends K9ListActivity {
if (account.equals(mAccount)) {
mHandler.progress(false);
if (Config.LOGV) {
Log.v(K9.LOG_TAG, "listFoldersFailed " + message);
}
}
super.listFoldersFailed(account, message);
}

View File

@ -3432,11 +3432,9 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
static class IdentityAdapter extends BaseAdapter {
private LayoutInflater mLayoutInflater;
private List<Object> mItems;
private FontSizes mFontSizes;
public IdentityAdapter(Context context, LayoutInflater layoutInflater) {
mLayoutInflater = layoutInflater;
mFontSizes = K9.getFontSizes();
List<Object> items = new ArrayList<Object>();
Preferences prefs = Preferences.getPreferences(context.getApplicationContext());

View File

@ -54,12 +54,10 @@ import android.widget.Toast;
import com.fsck.k9.Account;
import com.fsck.k9.Account.SortType;
import com.fsck.k9.AccountStats;
import com.fsck.k9.BaseAccount;
import com.fsck.k9.FontSizes;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
import com.fsck.k9.SearchAccount;
import com.fsck.k9.SearchSpecification;
import com.fsck.k9.activity.setup.AccountSettings;
import com.fsck.k9.activity.setup.FolderSettings;
@ -272,8 +270,6 @@ public class MessageList
private Account mAccount;
private int mUnreadMessageCount = 0;
private GestureDetector gestureDetector;
private View.OnTouchListener gestureListener;
/**
* Stores the name of the folder that we want to open as soon as possible
* after load.
@ -364,13 +360,13 @@ public class MessageList
@Override
public void run() {
for (MessageInfoHolder message : messages) {
if (message != null) {
if (mFolderName == null || (message.folder != null && message.folder.name.equals(mFolderName))) {
if (message.selected && mSelectedCount > 0) {
mSelectedCount--;
}
mAdapter.messages.remove(message);
if (message != null && (mFolderName == null || (
message.folder != null &&
message.folder.name.equals(mFolderName)))) {
if (message.selected && mSelectedCount > 0) {
mSelectedCount--;
}
mAdapter.messages.remove(message);
}
}
resetUnreadCountOnThread();

View File

@ -6,6 +6,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Vibrator;
import android.preference.*;
@ -50,6 +51,7 @@ public class AccountSettings extends K9PreferenceActivity {
private static final String PREFERENCE_SCREEN_COMPOSING = "composing";
private static final String PREFERENCE_SCREEN_INCOMING = "incoming_prefs";
private static final String PREFERENCE_SCREEN_PUSH_ADVANCED = "push_advanced";
private static final String PREFERENCE_SCREEN_NOTIFICATIONS = "notifications";
private static final String PREFERENCE_DESCRIPTION = "account_description";
private static final String PREFERENCE_MARK_MESSAGE_AS_READ_ON_VIEW = "mark_message_as_read_on_view";
@ -581,8 +583,23 @@ public class AccountSettings extends K9PreferenceActivity {
mNotificationOpensUnread = (CheckBoxPreference)findPreference(PREFERENCE_NOTIFICATION_OPENS_UNREAD);
mNotificationOpensUnread.setChecked(mAccount.goToUnreadMessageSearch());
mNotificationUnreadCount = (CheckBoxPreference)findPreference(PREFERENCE_NOTIFICATION_UNREAD_COUNT);
mNotificationUnreadCount.setChecked(mAccount.isNotificationShowsUnreadCount());
CheckBoxPreference notificationUnreadCount =
(CheckBoxPreference) findPreference(PREFERENCE_NOTIFICATION_UNREAD_COUNT);
/*
* Honeycomb and newer don't show the notification number as overlay on the notification
* icon in the status bar, so we hide the setting.
*
* See http://code.google.com/p/android/issues/detail?id=21477
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
PreferenceScreen notificationsPrefs =
(PreferenceScreen) findPreference(PREFERENCE_SCREEN_NOTIFICATIONS);
notificationsPrefs.removePreference(notificationUnreadCount);
} else {
notificationUnreadCount.setChecked(mAccount.isNotificationShowsUnreadCount());
mNotificationUnreadCount = notificationUnreadCount;
}
new PopulateFolderPrefsTask().execute();
@ -699,7 +716,9 @@ public class AccountSettings extends K9PreferenceActivity {
mAccount.getNotificationSetting().setVibrateTimes(Integer.parseInt(mAccountVibrateTimes.getValue()));
mAccount.getNotificationSetting().setLed(mAccountLed.isChecked());
mAccount.setGoToUnreadMessageSearch(mNotificationOpensUnread.isChecked());
mAccount.setNotificationShowsUnreadCount(mNotificationUnreadCount.isChecked());
if (mNotificationUnreadCount != null) {
mAccount.setNotificationShowsUnreadCount(mNotificationUnreadCount.isChecked());
}
mAccount.setFolderTargetMode(Account.FolderMode.valueOf(mTargetMode.getValue()));
Log.d("ASH", "Setting delete policy to " + mDeletePolicy.getValue());
mAccount.setDeletePolicy(Integer.parseInt(mDeletePolicy.getValue()));

View File

@ -319,11 +319,11 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
if (name.equalsIgnoreCase(storeURIHost) || name.equalsIgnoreCase(transportURIHost)) {
//TODO: localize this string
altNamesText.append("Subject(alt): ").append(name).append(",...\n");
} else if (name.startsWith("*.")) {
if (storeURIHost.endsWith(name.substring(2)) || transportURIHost.endsWith(name.substring(2))) {
//TODO: localize this string
altNamesText.append("Subject(alt): ").append(name).append(",...\n");
}
} else if (name.startsWith("*.") && (
storeURIHost.endsWith(name.substring(2)) ||
transportURIHost.endsWith(name.substring(2)))) {
//TODO: localize this string
altNamesText.append("Subject(alt): ").append(name).append(",...\n");
}
}
chainInfo.append(altNamesText);

View File

@ -33,6 +33,7 @@ import com.fsck.k9.preferences.CheckBoxListPreference;
import com.fsck.k9.preferences.TimePickerPreference;
import com.fsck.k9.service.MailService;
import com.fsck.k9.view.MessageWebView;
public class Prefs extends K9PreferenceActivity {
@ -284,13 +285,13 @@ public class Prefs extends K9PreferenceActivity {
mZoomControlsEnabled.setChecked(K9.zoomControlsEnabled());
mMobileOptimizedLayout = (CheckBoxPreference) findPreference(PREFERENCE_MESSAGEVIEW_MOBILE_LAYOUT);
if (Build.VERSION.SDK_INT <= 7) {
if (!MessageWebView.isSingleColumnLayoutSupported()) {
mMobileOptimizedLayout.setEnabled(false);
mMobileOptimizedLayout.setChecked(false);
} else {
mMobileOptimizedLayout.setChecked(K9.mobileOptimizedLayout());
}
mMobileOptimizedLayout.setChecked(K9.mobileOptimizedLayout());
mQuietTimeEnabled = (CheckBoxPreference) findPreference(PREFERENCE_QUIET_TIME_ENABLED);
mQuietTimeEnabled.setChecked(K9.getQuietTimeEnabled());
@ -320,6 +321,33 @@ public class Prefs extends K9PreferenceActivity {
mBackgroundOps = setupListPreference(PREFERENCE_BACKGROUND_OPS, K9.getBackgroundOps().toString());
// In ICS+ there is no 'background data' setting that apps can chose to ignore anymore. So
// we hide that option for "Background Sync".
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
CharSequence[] oldEntries = mBackgroundOps.getEntries();
CharSequence[] newEntries = new CharSequence[3];
// Use "When 'Auto-sync' is checked" instead of "When 'Background data' & 'Auto-sync'
// are checked" as description.
newEntries[0] = getString(R.string.background_ops_auto_sync_only);
newEntries[1] = oldEntries[2];
newEntries[2] = oldEntries[3];
CharSequence[] oldValues = mBackgroundOps.getEntryValues();
CharSequence[] newValues = new CharSequence[3];
newValues[0] = oldValues[1];
newValues[1] = oldValues[2];
newValues[2] = oldValues[3];
mBackgroundOps.setEntries(newEntries);
mBackgroundOps.setEntryValues(newValues);
// Since ConnectivityManager.getBackgroundDataSetting() always returns 'true' on ICS+
// we map WHEN_CHECKED to ALWAYS.
if (K9.getBackgroundOps() == K9.BACKGROUND_OPS.WHEN_CHECKED) {
mBackgroundOps.setValue(K9.BACKGROUND_OPS.ALWAYS.toString());
mBackgroundOps.setSummary(mBackgroundOps.getEntry());
}
}
mUseGalleryBugWorkaround = (CheckBoxPreference)findPreference(PREFERENCE_GALLERY_BUG_WORKAROUND);
mUseGalleryBugWorkaround.setChecked(K9.useGalleryBugWorkaround());

View File

@ -21,6 +21,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.PowerManager;
import android.os.Process;
import android.text.TextUtils;
@ -534,9 +535,6 @@ public class MessagingController implements Runnable {
List<Message> pendingMessages = new ArrayList<Message>();
int totalDone = 0;
@Override
public void messageStarted(String message, int number, int ofTotal) {}
@Override
@ -544,7 +542,6 @@ public class MessagingController implements Runnable {
if (!isMessageSuppressed(account, folder, message)) {
pendingMessages.add(message);
totalDone++;
if (pendingMessages.size() > 10) {
addPendingMessages();
}
@ -2317,13 +2314,14 @@ public class MessagingController implements Runnable {
* upto speed with the remote UIDs of remote destionation folder.
*/
if (!localUidMap.isEmpty() && remoteUidMap != null && !remoteUidMap.isEmpty()) {
Set<String> remoteSrcUids = remoteUidMap.keySet();
Iterator<String> remoteSrcUidsIterator = remoteSrcUids.iterator();
Set<Map.Entry<String, String>> remoteSrcEntries = remoteUidMap.entrySet();
Iterator<Map.Entry<String, String>> remoteSrcEntriesIterator = remoteSrcEntries.iterator();
while (remoteSrcUidsIterator.hasNext()) {
String remoteSrcUid = remoteSrcUidsIterator.next();
while (remoteSrcEntriesIterator.hasNext()) {
Map.Entry<String, String> entry = remoteSrcEntriesIterator.next();
String remoteSrcUid = entry.getKey();
String localDestUid = localUidMap.get(remoteSrcUid);
String newUid = remoteUidMap.get(remoteSrcUid);
String newUid = entry.getValue();
Message localDestMessage = localDestFolder.getMessage(localDestUid);
if (localDestMessage != null) {
@ -3433,7 +3431,6 @@ public class MessagingController implements Runnable {
// "don't even bother" functionality
if (getRootCauseMessage(e).startsWith("5")) {
localFolder.moveMessages(new Message[] { message }, (LocalFolder) localStore.getFolder(account.getDraftsFolderName()));
} else {
}
message.setFlag(Flag.X_SEND_FAILED, true);
@ -4562,7 +4559,10 @@ public class MessagingController implements Runnable {
builder.setTicker(messageNotice);
final int unreadCount = previousUnreadMessageCount + newMessageCount.get();
if (account.isNotificationShowsUnreadCount()) {
if (account.isNotificationShowsUnreadCount() ||
// Honeycomb and newer don't show the number as overlay on the notification icon.
// However, the number will appear in the detailed notification view.
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
builder.setNumber(unreadCount);
}

View File

@ -1,5 +1,6 @@
package com.fsck.k9.helper;
import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.Context;
import android.content.ClipboardManager;
@ -7,6 +8,7 @@ import android.content.ClipboardManager;
/**
* Access the system clipboard using the new {@link ClipboardManager} introduced with API 11
*/
@TargetApi(11)
public class ClipboardManagerApi11 extends com.fsck.k9.helper.ClipboardManager {
public ClipboardManagerApi11(Context context) {

View File

@ -124,16 +124,14 @@ public class DomainNameChecker {
List<?> altNameEntry = (List<?>)(subjectAltName);
if ((altNameEntry != null) && (2 <= altNameEntry.size())) {
Integer altNameType = (Integer)(altNameEntry.get(0));
if (altNameType != null) {
if (altNameType == ALT_IPA_NAME) {
String altName = (String)(altNameEntry.get(1));
if (altName != null) {
if (K9.DEBUG) {
Log.v(K9.LOG_TAG, "alternative IP: " + altName);
}
if (thisDomain.equalsIgnoreCase(altName)) {
return true;
}
if (altNameType != null && altNameType.intValue() == ALT_IPA_NAME) {
String altName = (String)(altNameEntry.get(1));
if (altName != null) {
if (K9.DEBUG) {
Log.v(K9.LOG_TAG, "alternative IP: " + altName);
}
if (thisDomain.equalsIgnoreCase(altName)) {
return true;
}
}
}
@ -166,15 +164,11 @@ public class DomainNameChecker {
List<?> altNameEntry = (List<?>)(i.next());
if ((altNameEntry != null) && (2 <= altNameEntry.size())) {
Integer altNameType = (Integer)(altNameEntry.get(0));
if (altNameType != null) {
if (altNameType.intValue() == ALT_DNS_NAME) {
hasDns = true;
String altName = (String)(altNameEntry.get(1));
if (altName != null) {
if (matchDns(thisDomain, altName)) {
return true;
}
}
if (altNameType != null && altNameType.intValue() == ALT_DNS_NAME) {
hasDns = true;
String altName = (String)(altNameEntry.get(1));
if (altName != null && matchDns(thisDomain, altName)) {
return true;
}
}
}

View File

@ -1,5 +1,6 @@
package com.fsck.k9.helper;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
@ -9,6 +10,7 @@ import android.net.Uri;
/**
* Create notifications using the new {@link android.app.Notification.Builder} class.
*/
@TargetApi(11)
public class NotificationBuilderApi11 extends NotificationBuilder {
private Notification.Builder mBuilder;

View File

@ -1,28 +0,0 @@
package com.fsck.k9.mail.filter;
import android.util.Config;
import android.util.Log;
import com.fsck.k9.K9;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class StatusOutputStream extends FilterOutputStream {
private long mCount = 0;
public StatusOutputStream(OutputStream out) {
super(out);
}
@Override
public void write(int oneByte) throws IOException {
super.write(oneByte);
mCount++;
if (Config.LOGV) {
if (mCount % 1024 == 0) {
Log.v(K9.LOG_TAG, "# " + mCount);
}
}
}
}

View File

@ -122,10 +122,9 @@ public class MimeHeader {
public boolean hasToBeEncoded(String text) {
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c < 0x20 || 0x7e < c) { // non printable
if (c != 0x0a && c != 0x0d && c != 0x09) { // non LF/CR/TAB
return true;
}
if ((c < 0x20 || 0x7e < c) && // non printable
(c != 0x0a && c != 0x0d && c != 0x09)) { // non LF/CR/TAB
return true;
}
}

View File

@ -384,6 +384,10 @@ public class ImapResponseParser {
return (String)get(index);
}
public long getLong(int index) {
return Long.parseLong(getString(index));
}
public int getNumber(int index) {
return Integer.parseInt(getString(index));
}

View File

@ -843,12 +843,12 @@ public class ImapStore extends Store {
class ImapFolder extends Folder {
private String mName;
protected volatile int mMessageCount = -1;
protected volatile int uidNext = -1;
protected volatile long uidNext = -1L;
protected volatile ImapConnection mConnection;
private OpenMode mMode;
private volatile boolean mExists;
private ImapStore store = null;
Map<Integer, String> msgSeqUidMap = new ConcurrentHashMap<Integer, String>();
Map<Long, String> msgSeqUidMap = new ConcurrentHashMap<Long, String>();
public ImapFolder(ImapStore nStore, String name) {
@ -1350,7 +1350,7 @@ public class ImapStore extends Store {
return getRemoteMessageCount("FLAGGED NOT DELETED");
}
protected int getHighestUid() {
protected long getHighestUid() {
try {
ImapSearcher searcher = new ImapSearcher() {
public List<ImapResponse> search() throws IOException, MessagingException {
@ -1359,12 +1359,12 @@ public class ImapStore extends Store {
};
Message[] messages = search(searcher, null);
if (messages.length > 0) {
return Integer.parseInt(messages[0].getUid());
return Long.parseLong(messages[0].getUid());
}
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Unable to find highest UID in folder " + getName(), e);
}
return -1;
return -1L;
}
@ -1404,7 +1404,7 @@ public class ImapStore extends Store {
return search(searcher, listener);
}
protected Message[] getMessages(final List<Integer> mesgSeqs, final boolean includeDeleted, final MessageRetrievalListener listener)
protected Message[] getMessages(final List<Long> mesgSeqs, final boolean includeDeleted, final MessageRetrievalListener listener)
throws MessagingException {
ImapSearcher searcher = new ImapSearcher() {
public List<ImapResponse> search() throws IOException, MessagingException {
@ -1429,13 +1429,13 @@ public class ImapStore extends Store {
checkOpen();
ArrayList<Message> messages = new ArrayList<Message>();
try {
ArrayList<Integer> uids = new ArrayList<Integer>();
ArrayList<Long> uids = new ArrayList<Long>();
List<ImapResponse> responses = searcher.search(); //
for (ImapResponse response : responses) {
if (response.mTag == null) {
if (ImapResponseParser.equalsIgnoreCase(response.get(0), "SEARCH")) {
for (int i = 1, count = response.size(); i < count; i++) {
uids.add(Integer.parseInt(response.getString(i)));
uids.add(response.getLong(i));
}
}
}
@ -1444,10 +1444,11 @@ public class ImapStore extends Store {
// Sort the uids in numerically ascending order
Collections.sort(uids);
for (int i = 0, count = uids.size(); i < count; i++) {
String uid = uids.get(i).toString();
if (listener != null) {
listener.messageStarted("" + uids.get(i), i, count);
listener.messageStarted(uid, i, count);
}
ImapMessage message = new ImapMessage("" + uids.get(i), this);
ImapMessage message = new ImapMessage(uid, this);
messages.add(message);
if (listener != null) {
listener.messageFinished(message, i, count);
@ -1571,7 +1572,7 @@ public class ImapStore extends Store {
if (response.mTag == null && ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
ImapList fetchList = (ImapList)response.getKeyedValue("FETCH");
String uid = fetchList.getKeyedString("UID");
int msgSeq = response.getNumber(0);
long msgSeq = response.getLong(0);
if (uid != null) {
try {
msgSeqUidMap.put(msgSeq, uid);
@ -1798,7 +1799,7 @@ public class ImapStore extends Store {
if (keyObj instanceof String) {
String key = (String)keyObj;
if ("UIDNEXT".equalsIgnoreCase(key)) {
uidNext = bracketed.getNumber(1);
uidNext = bracketed.getLong(1);
if (K9.DEBUG)
Log.d(K9.LOG_TAG, "Got UidNext = " + uidNext + " for " + getLogId());
}
@ -2190,10 +2191,10 @@ public class ImapStore extends Store {
public String getNewPushState(String oldPushStateS, Message message) {
try {
String messageUidS = message.getUid();
int messageUid = Integer.parseInt(messageUidS);
long messageUid = Long.parseLong(messageUidS);
ImapPushState oldPushState = ImapPushState.parse(oldPushStateS);
if (messageUid >= oldPushState.uidNext) {
int uidNext = messageUid + 1;
long uidNext = messageUid + 1;
ImapPushState newPushState = new ImapPushState(uidNext);
return newPushState.toString();
} else {
@ -2303,21 +2304,19 @@ public class ImapStore extends Store {
capabilityList = response;
}
if (capabilityList != null) {
if (!capabilityList.isEmpty() && ImapResponseParser.equalsIgnoreCase(capabilityList.get(0), CAPABILITY_CAPABILITY)) {
if (K9.DEBUG) {
Log.d(K9.LOG_TAG, "Saving " + capabilityList.size() + " capabilities for " + getLogId());
if (capabilityList != null && !capabilityList.isEmpty() &&
ImapResponseParser.equalsIgnoreCase(capabilityList.get(0), CAPABILITY_CAPABILITY)) {
if (K9.DEBUG) {
Log.d(K9.LOG_TAG, "Saving " + capabilityList.size() + " capabilities for " + getLogId());
}
for (Object capability : capabilityList) {
if (capability instanceof String) {
// if (K9.DEBUG)
// {
// Log.v(K9.LOG_TAG, "Saving capability '" + capability + "' for " + getLogId());
// }
capabilities.add(((String)capability).toUpperCase(Locale.US));
}
for (Object capability : capabilityList) {
if (capability instanceof String) {
// if (K9.DEBUG)
// {
// Log.v(K9.LOG_TAG, "Saving capability '" + capability + "' for " + getLogId());
// }
capabilities.add(((String)capability).toUpperCase(Locale.US));
}
}
}
}
}
@ -2886,7 +2885,7 @@ public class ImapStore extends Store {
while (!stop.get()) {
try {
int oldUidNext = -1;
long oldUidNext = -1L;
try {
String pushStateS = receiver.getPushState(getName());
ImapPushState pushState = ImapPushState.parse(pushStateS);
@ -2922,16 +2921,16 @@ public class ImapStore extends Store {
if (stop.get()) {
continue;
}
int startUid = oldUidNext;
long startUid = oldUidNext;
int newUidNext = uidNext;
long newUidNext = uidNext;
if (newUidNext == -1) {
if (K9.DEBUG) {
Log.d(K9.LOG_TAG, "uidNext is -1, using search to find highest UID");
}
int highestUid = getHighestUid();
if (highestUid != -1) {
long highestUid = getHighestUid();
if (highestUid != -1L) {
if (K9.DEBUG)
Log.d(K9.LOG_TAG, "highest UID = " + highestUid);
newUidNext = highestUid + 1;
@ -2952,7 +2951,7 @@ public class ImapStore extends Store {
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Needs sync from uid " + startUid + " to " + newUidNext + " for " + getLogId());
List<Message> messages = new ArrayList<Message>();
for (int uid = startUid; uid < newUidNext; uid++) {
for (long uid = startUid; uid < newUidNext; uid++) {
ImapMessage message = new ImapMessage("" + uid, ImapFolderPusher.this);
messages.add(message);
}
@ -3052,7 +3051,7 @@ public class ImapStore extends Store {
if (oldMessageCount == -1) {
skipSync = true;
}
List<Integer> flagSyncMsgSeqs = new ArrayList<Integer>();
List<Long> flagSyncMsgSeqs = new ArrayList<Long>();
List<String> removeMsgUids = new LinkedList<String>();
for (ImapResponse response : responses) {
@ -3078,7 +3077,7 @@ public class ImapStore extends Store {
}
private void syncMessages(int end, boolean newArrivals) throws MessagingException {
int oldUidNext = -1;
long oldUidNext = -1L;
try {
String pushStateS = receiver.getPushState(getName());
ImapPushState pushState = ImapPushState.parse(pushStateS);
@ -3091,10 +3090,10 @@ public class ImapStore extends Store {
Message[] messageArray = getMessages(end, end, null, true, null);
if (messageArray != null && messageArray.length > 0) {
int newUid = Integer.parseInt(messageArray[0].getUid());
long newUid = Long.parseLong(messageArray[0].getUid());
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Got newUid " + newUid + " for message " + end + " on " + getLogId());
int startUid = oldUidNext;
long startUid = oldUidNext;
if (startUid < newUid - 10) {
startUid = newUid - 10;
}
@ -3106,8 +3105,8 @@ public class ImapStore extends Store {
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Needs sync from uid " + startUid + " to " + newUid + " for " + getLogId());
List<Message> messages = new ArrayList<Message>();
for (int uid = startUid; uid <= newUid; uid++) {
ImapMessage message = new ImapMessage("" + uid, ImapFolderPusher.this);
for (long uid = startUid; uid <= newUid; uid++) {
ImapMessage message = new ImapMessage(Long.toString(uid), ImapFolderPusher.this);
messages.add(message);
}
if (!messages.isEmpty()) {
@ -3117,7 +3116,7 @@ public class ImapStore extends Store {
}
}
private void syncMessages(List<Integer> flagSyncMsgSeqs) {
private void syncMessages(List<Long> flagSyncMsgSeqs) {
try {
Message[] messageArray = null;
@ -3160,7 +3159,7 @@ public class ImapStore extends Store {
}
protected int processUntaggedResponse(int oldMessageCount, ImapResponse response, List<Integer> flagSyncMsgSeqs, List<String> removeMsgUids) {
protected int processUntaggedResponse(long oldMessageCount, ImapResponse response, List<Long> flagSyncMsgSeqs, List<String> removeMsgUids) {
super.handleUntaggedResponse(response);
int messageCountDelta = 0;
if (response.mTag == null && response.size() > 1) {
@ -3168,7 +3167,7 @@ public class ImapStore extends Store {
Object responseType = response.get(1);
if (ImapResponseParser.equalsIgnoreCase(responseType, "FETCH")) {
Log.i(K9.LOG_TAG, "Got FETCH " + response);
int msgSeq = response.getNumber(0);
long msgSeq = response.getLong(0);
if (K9.DEBUG)
Log.d(K9.LOG_TAG, "Got untagged FETCH for msgseq " + msgSeq + " for " + getLogId());
@ -3178,17 +3177,17 @@ public class ImapStore extends Store {
}
}
if (ImapResponseParser.equalsIgnoreCase(responseType, "EXPUNGE")) {
int msgSeq = response.getNumber(0);
long msgSeq = response.getLong(0);
if (msgSeq <= oldMessageCount) {
messageCountDelta = -1;
}
if (K9.DEBUG)
Log.d(K9.LOG_TAG, "Got untagged EXPUNGE for msgseq " + msgSeq + " for " + getLogId());
List<Integer> newSeqs = new ArrayList<Integer>();
Iterator<Integer> flagIter = flagSyncMsgSeqs.iterator();
List<Long> newSeqs = new ArrayList<Long>();
Iterator<Long> flagIter = flagSyncMsgSeqs.iterator();
while (flagIter.hasNext()) {
Integer flagMsg = flagIter.next();
long flagMsg = flagIter.next();
if (flagMsg >= msgSeq) {
flagIter.remove();
if (flagMsg > msgSeq) {
@ -3199,14 +3198,13 @@ public class ImapStore extends Store {
flagSyncMsgSeqs.addAll(newSeqs);
List<Integer> msgSeqs = new ArrayList<Integer>(msgSeqUidMap.keySet());
List<Long> msgSeqs = new ArrayList<Long>(msgSeqUidMap.keySet());
Collections.sort(msgSeqs); // Have to do comparisons in order because of msgSeq reductions
for (Integer msgSeqNumI : msgSeqs) {
for (long msgSeqNum : msgSeqs) {
if (K9.DEBUG) {
Log.v(K9.LOG_TAG, "Comparing EXPUNGEd msgSeq " + msgSeq + " to " + msgSeqNumI);
Log.v(K9.LOG_TAG, "Comparing EXPUNGEd msgSeq " + msgSeq + " to " + msgSeqNum);
}
int msgSeqNum = msgSeqNumI;
if (msgSeqNum == msgSeq) {
String uid = msgSeqUidMap.get(msgSeqNum);
if (K9.DEBUG) {
@ -3388,12 +3386,12 @@ public class ImapStore extends Store {
}
protected static class ImapPushState {
protected int uidNext;
protected ImapPushState(int nUidNext) {
protected long uidNext;
protected ImapPushState(long nUidNext) {
uidNext = nUidNext;
}
protected static ImapPushState parse(String pushState) {
int newUidNext = -1;
long newUidNext = -1L;
if (pushState != null) {
StringTokenizer tokenizer = new StringTokenizer(pushState, ";");
while (tokenizer.hasMoreTokens()) {
@ -3404,8 +3402,8 @@ public class ImapStore extends Store {
if ("uidNext".equalsIgnoreCase(key) && thisState.hasMoreTokens()) {
String value = thisState.nextToken();
try {
newUidNext = Integer.parseInt(value);
} catch (Exception e) {
newUidNext = Long.parseLong(value);
} catch (NumberFormatException e) {
Log.e(K9.LOG_TAG, "Unable to part uidNext value " + value, e);
}

View File

@ -157,216 +157,219 @@ public class LocalStore extends Store implements Serializable {
AttachmentProvider.clear(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) {
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, "
+ "local_only INTEGER default 0)");
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, "
+ "local_only INTEGER default 0)");
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)");
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)");
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("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 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("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)");
}
if (db.getVersion() < 32) {
db.execSQL("UPDATE messages SET deleted = 1 WHERE flags LIKE '%DELETED%'");
}
if (db.getVersion() < 33) {
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)");
try {
db.execSQL("ALTER TABLE messages ADD preview TEXT");
} catch (SQLiteException e) {
if (! e.toString().startsWith("duplicate column name: preview")) {
throw e;
}
}
db.execSQL("DROP TABLE IF EXISTS pending_commands");
db.execSQL("CREATE TABLE pending_commands " +
"(id INTEGER PRIMARY KEY, command TEXT, arguments TEXT)");
}
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");
}
}
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;");
// 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");
}
}
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
// 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 = 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);
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;
}
}
}
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() < 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() == 41) {
try {
long startTime = System.currentTimeMillis();
SharedPreferences.Editor editor = getPreferences().edit();
if (db.getVersion() < 32) {
db.execSQL("UPDATE messages SET deleted = 1 WHERE flags LIKE '%DELETED%'");
}
if (db.getVersion() < 33) {
List <? extends Folder > folders = getPersonalNamespaces(true);
for (Folder folder : folders) {
if (folder instanceof LocalFolder) {
LocalFolder lFolder = (LocalFolder)folder;
lFolder.save(editor);
try {
db.execSQL("ALTER TABLE messages ADD preview TEXT");
} catch (SQLiteException e) {
if (! e.toString().startsWith("duplicate column name: preview")) {
throw e;
}
}
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("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);
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 = 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);
}
}
}
// 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(localizedOutbox);
if (obsoleteOutbox.exists()) {
// Get all messages from the localized outbox ...
Message[] messages = obsoleteOutbox.getMessages(null, false);
if (messages.length > 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(mAccount.getDraftsFolderName());
obsoleteOutbox.moveMessages(messages, drafts);
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 = getPreferences().edit();
List <? extends Folder > folders = getPersonalNamespaces(true);
for (Folder folder : folders) {
if (folder instanceof LocalFolder) {
LocalFolder lFolder = (LocalFolder)folder;
lFolder.save(editor);
}
}
// Now get rid of the localized outbox
obsoleteOutbox.delete();
obsoleteOutbox.delete(true);
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("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(localizedOutbox);
if (obsoleteOutbox.exists()) {
// Get all messages from the localized outbox ...
Message[] messages = obsoleteOutbox.getMessages(null, false);
if (messages.length > 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(mAccount.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);
}
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Error trying to fix the outbox folders", e);
}
}
if (db.getVersion() < 44) {
@ -410,19 +413,19 @@ Log.d("ASH", "updatedb " + mAccount.getDescription());
+ e);
}
}
} catch (SQLiteException e) {
Log.e(K9.LOG_TAG, "Exception while upgrading database. Resetting the DB to v0");
db.setVersion(0);
throw new Error("Database upgrade failed! Resetting your DB version to 0 to force a full schema recreation.");
}
db.setVersion(DB_VERSION);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
catch (SQLiteException e) {
Log.e(K9.LOG_TAG, "Exception while upgrading database. Resetting the DB to v0");
db.setVersion(0);
throw new Error("Database upgrade failed! Resetting your DB version to 0 to force a full schema recreation.");
}
db.setVersion(DB_VERSION);
if (db.getVersion() != DB_VERSION) {
throw new Error("Database upgrade failed!");
}
@ -3496,10 +3499,8 @@ Log.v("ASH", mAccount.getDescription() + ":" + name + " is " + (localOnly == 1 ?
if (!isSet(Flag.DELETED)) {
if (flag == Flag.SEEN) {
if (set != isSet(Flag.SEEN)) {
folder.setUnreadMessageCount(folder.getUnreadMessageCount() + (set ? -1 : 1));
}
if (flag == Flag.SEEN && set != isSet(Flag.SEEN)) {
folder.setUnreadMessageCount(folder.getUnreadMessageCount() + (set ? -1 : 1));
}
if (flag == Flag.FLAGGED) {

View File

@ -1996,11 +1996,6 @@ public class WebDavStore extends Store {
return false;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public String getUidFromMessageId(Message message) throws MessagingException {
Log.e(K9.LOG_TAG,

View File

@ -54,11 +54,8 @@ public class ImapUtility {
for (String item : setItems) {
if (item.indexOf(':') == -1) {
// simple item
try {
Integer.parseInt(item); // Don't need the value; just ensure it's valid
if (isNumberValid(item)) {
list.add(item);
} catch (NumberFormatException e) {
Log.d(K9.LOG_TAG, "Invalid UID value", e);
}
} else {
// range
@ -91,23 +88,46 @@ public class ImapUtility {
if (range != null) {
int colonPos = range.indexOf(':');
if (colonPos > 0) {
int first = Integer.parseInt(range.substring(0, colonPos));
int second = Integer.parseInt(range.substring(colonPos + 1));
if (first < second) {
for (int i = first; i <= second; i++) {
list.add(Integer.toString(i));
long first = Long.parseLong(range.substring(0, colonPos));
long second = Long.parseLong(range.substring(colonPos + 1));
if (is32bitValue(first) && is32bitValue(second)) {
if (first < second) {
for (long i = first; i <= second; i++) {
list.add(Long.toString(i));
}
} else {
for (long i = first; i >= second; i--) {
list.add(Long.toString(i));
}
}
} else {
for (int i = first; i >= second; i--) {
list.add(Integer.toString(i));
}
Log.d(K9.LOG_TAG, "Invalid range: " + range);
}
}
}
} catch (NumberFormatException e) {
Log.d(K9.LOG_TAG, "Invalid range value", e);
Log.d(K9.LOG_TAG, "Invalid range value: " + range, e);
}
return list;
}
private static boolean isNumberValid(String number) {
try {
long value = Long.parseLong(number);
if (is32bitValue(value)) {
return true;
}
} catch (NumberFormatException e) {
// do nothing
}
Log.d(K9.LOG_TAG, "Invalid UID value: " + number);
return false;
}
private static boolean is32bitValue(long value) {
return ((value & ~0xFFFFFFFFL) == 0L);
}
}

View File

@ -188,7 +188,6 @@ public class SmtpTransport extends Transport {
String mPassword;
String mAuthType;
int mConnectionSecurity;
boolean mSecure;
Socket mSocket;
PeekableInputStream mIn;
OutputStream mOut;
@ -245,7 +244,6 @@ public class SmtpTransport extends Transport {
}, new SecureRandom());
mSocket = sslContext.getSocketFactory().createSocket();
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
mSecure = true;
} else {
mSocket = new Socket();
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
@ -308,7 +306,6 @@ public class SmtpTransport extends Transport {
mIn = new PeekableInputStream(new BufferedInputStream(mSocket.getInputStream(),
1024));
mOut = mSocket.getOutputStream();
mSecure = true;
/*
* Now resend the EHLO. Required by RFC2487 Sec. 5.2, and more specifically,
* Exim.

View File

@ -1,5 +1,6 @@
package com.fsck.k9.view;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
@ -24,6 +25,27 @@ public class MessageWebView extends WebView {
*/
public static final Method mGetBlockNetworkLoads = K9.getMethod(WebSettings.class, "setBlockNetworkLoads");
/**
* Check whether the single column layout algorithm can be used on this version of Android.
*
* <p>
* Single column layout was broken on Android < 2.2 (see
* <a href="http://code.google.com/p/android/issues/detail?id=5024">issue 5024</a>).
* </p>
*
* <p>
* Android versions >= 3.0 have problems with unclickable links when single column layout is
* enabled (see
* <a href="http://code.google.com/p/android/issues/detail?id=34886">issue 34886</a>
* in Android's bug tracker, and
* <a href="http://code.google.com/p/k9mail/issues/detail?id=3820">issue 3820</a>
* in K-9 Mail's bug tracker).
*/
public static boolean isSingleColumnLayoutSupported() {
return (Build.VERSION.SDK_INT > 7 && Build.VERSION.SDK_INT < 11);
}
public MessageWebView(Context context) {
super(context);
}
@ -90,24 +112,13 @@ public class MessageWebView extends WebView {
webSettings.setBuiltInZoomControls(true);
}
// SINGLE_COLUMN layout was broken on Android < 2.2, so we
// administratively disable it
if (Build.VERSION.SDK_INT > 7 && K9.mobileOptimizedLayout()) {
if (isSingleColumnLayoutSupported() && K9.mobileOptimizedLayout()) {
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
} else {
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
}
/*
* Build.VERSION.SDK is deprecated cause it just returns the
* "its raw String representation"
* http://developer.android.com/reference/android/os/Build.VERSION_CODES.html#GINGERBREAD
* http://developer.android.com/reference/android/os/Build.VERSION.html#SDK
*/
if (Build.VERSION.SDK_INT >= 9) {
setOverScrollMode(OVER_SCROLL_NEVER);
}
disableOverscrolling();
webSettings.setTextSize(K9.getFontSizes().getMessageViewContent());
@ -116,6 +127,13 @@ public class MessageWebView extends WebView {
}
@TargetApi(9)
private void disableOverscrolling() {
if (Build.VERSION.SDK_INT >= 9) {
setOverScrollMode(OVER_SCROLL_NEVER);
}
}
public void setText(String text, String contentType) {
String content = text;
if (K9.getK9Theme() == K9.THEME_DARK) {

View File

@ -104,6 +104,7 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
private LinearLayout mInsideAttachmentsContainer;
private SavedState mSavedState;
private ClipboardManager mClipboardManager;
private String mText;
public void initialize(Activity activity) {
@ -400,7 +401,10 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
break;
}
case R.id.show_pictures: {
// Allow network access first...
setLoadPictures(true);
// ...then re-populate the WebView with the message text
loadBodyFromText(mText, "text/html");
break;
}
}
@ -538,28 +542,20 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
MessagingController controller, MessagingListener listener) throws MessagingException {
resetView();
String type;
String text = null;
if (pgpData != null) {
text = pgpData.getDecryptedData();
}
if (text != null) {
type = "text/html";
text = "<html><body><pre>" + text + "</pre></body></html>";
} else {
// getTextForDisplay() always returns HTML-ified content.
text = message.getTextForDisplay();
type = "text/html";
}
if (text != null) {
final String emailText = text;
final String contentType = type;
loadBodyFromText(emailText, contentType);
updateCryptoLayout(account.getCryptoProvider(), pgpData, message);
} else {
showStatusMessage(getContext().getString(R.string.webview_empty_message));
}
// Save the text so we can reset the WebView when the user clicks the "Show pictures" button
mText = text;
mHasAttachments = message.hasAttachments();
if (mHasAttachments) {
@ -607,6 +603,13 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
}
}
}
if (text != null) {
loadBodyFromText(text, "text/html");
updateCryptoLayout(account.getCryptoProvider(), pgpData, message);
} else {
showStatusMessage(getContext().getString(R.string.webview_empty_message));
}
}
public void showStatusMessage(String status) {

View File

@ -34,6 +34,14 @@ public class ImapUtilityTest extends TestCase {
actual = ImapUtility.getImapSequenceValues("1");
MoreAsserts.assertEquals(expected, actual.toArray());
expected = new String[] {"2147483648"}; // Integer.MAX_VALUE + 1
actual = ImapUtility.getImapSequenceValues("2147483648");
MoreAsserts.assertEquals(expected, actual.toArray());
expected = new String[] {"4294967295"}; // 2^32 - 1
actual = ImapUtility.getImapSequenceValues("4294967295");
MoreAsserts.assertEquals(expected, actual.toArray());
expected = new String[] {"1", "3", "2"};
actual = ImapUtility.getImapSequenceValues("1,3,2");
MoreAsserts.assertEquals(expected, actual.toArray());
@ -50,6 +58,11 @@ public class ImapUtilityTest extends TestCase {
actual = ImapUtility.getImapSequenceValues("1,2:4,9:7");
MoreAsserts.assertEquals(expected, actual.toArray());
// Test numbers larger than Integer.MAX_VALUE (2147483647)
expected = new String[] {"2147483646", "2147483647", "2147483648"};
actual = ImapUtility.getImapSequenceValues("2147483646:2147483648");
MoreAsserts.assertEquals(expected, actual.toArray());
// Test partially invalid sets
expected = new String[] { "1", "5" };
actual = ImapUtility.getImapSequenceValues("1,x,5");
@ -75,6 +88,15 @@ public class ImapUtilityTest extends TestCase {
expected = new String[0];
actual = ImapUtility.getImapSequenceValues("1:x");
MoreAsserts.assertEquals(expected, actual.toArray());
// Test values larger than 2^32 - 1
expected = new String[0];
actual = ImapUtility.getImapSequenceValues("4294967296:4294967297");
MoreAsserts.assertEquals(expected, actual.toArray());
expected = new String[0];
actual = ImapUtility.getImapSequenceValues("4294967296"); // 2^32
MoreAsserts.assertEquals(expected, actual.toArray());
}
/**