searchRemoteMessages(String queryString,
+ String folder, final Flag[] requiredFlags, final Flag[] forbiddenFlags) throws MessagingException {
+ throw new MessagingException("K-9 does not support remote searching on this account type");
+ }
}
diff --git a/src/com/fsck/k9/mail/internet/MimeMessage.java b/src/com/fsck/k9/mail/internet/MimeMessage.java
index d61d85f00..8538bc7e6 100644
--- a/src/com/fsck/k9/mail/internet/MimeMessage.java
+++ b/src/com/fsck/k9/mail/internet/MimeMessage.java
@@ -592,4 +592,28 @@ public class MimeMessage extends Message {
copy(message);
return message;
}
+
+ public boolean toMe() {
+ return false;
+ }
+
+ public boolean ccMe() {
+ return false;
+ }
+
+ public boolean bccMe() {
+ return false;
+ }
+
+ public long getId() {
+ return Long.parseLong(mUid); //or maybe .mMessageId?
+ }
+
+ public String getPreview() {
+ return "";
+ }
+
+ public boolean hasAttachments() {
+ return false;
+ }
}
diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java
index 307d52e5b..0fcc18dc6 100644
--- a/src/com/fsck/k9/mail/store/ImapStore.java
+++ b/src/com/fsck/k9/mail/store/ImapStore.java
@@ -45,11 +45,15 @@ import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
+import org.apache.commons.io.IOUtils;
+
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@@ -73,13 +77,14 @@ import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.FetchProfile;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder;
+import com.fsck.k9.mail.Folder.OpenMode;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.PushReceiver;
import com.fsck.k9.mail.Pusher;
-import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.ServerSettings;
+import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
import com.fsck.k9.mail.filter.FixedLengthInputStream;
import com.fsck.k9.mail.filter.PeekableInputStream;
@@ -94,9 +99,6 @@ import com.fsck.k9.mail.store.imap.ImapUtility;
import com.fsck.k9.mail.transport.imap.ImapSettings;
import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZOutputStream;
-import java.util.zip.Inflater;
-import java.util.zip.InflaterInputStream;
-import org.apache.commons.io.IOUtils;
/**
*
@@ -1127,7 +1129,7 @@ public class ImapStore extends Store {
}
ImapFolder iFolder = (ImapFolder)folder;
- checkOpen();
+ checkOpen(); //only need READ access
String[] uids = new String[messages.length];
for (int i = 0, count = messages.length; i < count; i++) {
@@ -1253,7 +1255,7 @@ public class ImapStore extends Store {
private int getRemoteMessageCount(String criteria) throws MessagingException {
- checkOpen();
+ checkOpen(); //only need READ access
try {
int count = 0;
int start = 1;
@@ -1289,7 +1291,7 @@ public class ImapStore extends Store {
return executeSimpleCommand("UID SEARCH *:*");
}
};
- Message[] messages = search(searcher, null);
+ Message[] messages = search(searcher, null).toArray(EMPTY_MESSAGE_ARRAY);
if (messages.length > 0) {
return Long.parseLong(messages[0].getUid());
}
@@ -1338,7 +1340,7 @@ public class ImapStore extends Store {
return executeSimpleCommand(String.format("UID SEARCH %d:%d%s%s", start, end, dateSearchString, includeDeleted ? "" : " NOT DELETED"));
}
};
- return search(searcher, listener);
+ return search(searcher, listener).toArray(EMPTY_MESSAGE_ARRAY);
}
protected Message[] getMessages(final List mesgSeqs, final boolean includeDeleted, final MessageRetrievalListener listener)
@@ -1348,7 +1350,7 @@ public class ImapStore extends Store {
return executeSimpleCommand(String.format("UID SEARCH %s%s", Utility.combine(mesgSeqs.toArray(), ','), includeDeleted ? "" : " NOT DELETED"));
}
};
- return search(searcher, listener);
+ return search(searcher, listener).toArray(EMPTY_MESSAGE_ARRAY);
}
protected Message[] getMessagesFromUids(final List mesgUids, final boolean includeDeleted, final MessageRetrievalListener listener)
@@ -1358,12 +1360,12 @@ public class ImapStore extends Store {
return executeSimpleCommand(String.format("UID SEARCH UID %s%s", Utility.combine(mesgUids.toArray(), ','), includeDeleted ? "" : " NOT DELETED"));
}
};
- return search(searcher, listener);
+ return search(searcher, listener).toArray(EMPTY_MESSAGE_ARRAY);
}
- private Message[] search(ImapSearcher searcher, MessageRetrievalListener listener) throws MessagingException {
+ private List search(ImapSearcher searcher, MessageRetrievalListener listener) throws MessagingException {
- checkOpen();
+ checkOpen(); //only need READ access
ArrayList messages = new ArrayList();
try {
ArrayList uids = new ArrayList();
@@ -1378,8 +1380,12 @@ public class ImapStore extends Store {
}
}
- // Sort the uids in numerically ascending order
- Collections.sort(uids);
+ // Sort the uids in numerically decreasing order
+ // By doing it in decreasing order, we ensure newest messages are dealt with first
+ // This makes the most sense when a limit is imposed, and also prevents UI from going
+ // crazy adding stuff at the top.
+ Collections.sort(uids, Collections.reverseOrder());
+
for (int i = 0, count = uids.size(); i < count; i++) {
String uid = uids.get(i).toString();
if (listener != null) {
@@ -1394,7 +1400,7 @@ public class ImapStore extends Store {
} catch (IOException ioe) {
throw ioExceptionHandler(mConnection, ioe);
}
- return messages.toArray(EMPTY_MESSAGE_ARRAY);
+ return messages;
}
@@ -1406,7 +1412,7 @@ public class ImapStore extends Store {
@Override
public Message[] getMessages(String[] uids, MessageRetrievalListener listener)
throws MessagingException {
- checkOpen();
+ checkOpen(); //only need READ access
ArrayList messages = new ArrayList();
try {
if (uids == null) {
@@ -1443,7 +1449,7 @@ public class ImapStore extends Store {
if (messages == null || messages.length == 0) {
return;
}
- checkOpen();
+ checkOpen(); //only need READ access
List uids = new ArrayList(messages.length);
HashMap messageMap = new HashMap();
for (int i = 0, count = messages.length; i < count; i++) {
@@ -1568,7 +1574,7 @@ public class ImapStore extends Store {
@Override
public void fetchPart(Message message, Part part, MessageRetrievalListener listener)
throws MessagingException {
- checkOpen();
+ checkOpen(); //only need READ access
String[] parts = part.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA);
if (parts == null) {
@@ -1971,6 +1977,7 @@ public class ImapStore extends Store {
*/
@Override
public Map appendMessages(Message[] messages) throws MessagingException {
+ open(OpenMode.READ_WRITE);
checkOpen();
try {
Map uidMap = new HashMap();
@@ -2083,6 +2090,7 @@ public class ImapStore extends Store {
@Override
public void expunge() throws MessagingException {
+ open(OpenMode.READ_WRITE);
checkOpen();
try {
executeSimpleCommand("EXPUNGE");
@@ -2115,6 +2123,7 @@ public class ImapStore extends Store {
@Override
public void setFlags(Flag[] flags, boolean value)
throws MessagingException {
+ open(OpenMode.READ_WRITE);
checkOpen();
@@ -2149,6 +2158,7 @@ public class ImapStore extends Store {
@Override
public void setFlags(Message[] messages, Flag[] flags, boolean value)
throws MessagingException {
+ open(OpenMode.READ_WRITE);
checkOpen();
String[] uids = new String[messages.length];
for (int i = 0, count = messages.length; i < count; i++) {
@@ -3415,4 +3425,105 @@ public class ImapStore extends Store {
}
}
+ @Override
+ public List searchRemoteMessages(final String queryString,
+ final String folderName, final Flag[] requiredFlags, final Flag[] forbiddenFlags) throws MessagingException {
+
+ if (!mAccount.allowRemoteSearch()) {
+ throw new MessagingException("Your settings do not allow remote searching of this account");
+ }
+
+ final ImapFolder folder = (ImapFolder) getFolder(folderName);
+ if (folder == null) {
+ throw new MessagingException("Invalid folder specified");
+ }
+
+ try {
+ folder.open(OpenMode.READ_ONLY);
+ folder.checkOpen();
+
+
+ ImapSearcher searcher = new ImapSearcher() {
+ public List search() throws IOException, MessagingException {
+ String imapQuery = "UID SEARCH ";
+ if (requiredFlags != null) {
+ for (Flag f : requiredFlags) {
+ switch (f) {
+ case DELETED:
+ imapQuery += "DELETED ";
+ break;
+
+ case SEEN:
+ imapQuery += "SEEN ";
+ break;
+
+ case ANSWERED:
+ imapQuery += "ANSWERED ";
+ break;
+
+ case FLAGGED:
+ imapQuery += "FLAGGED ";
+ break;
+
+ case DRAFT:
+ imapQuery += "DRAFT ";
+ break;
+
+ case RECENT:
+ imapQuery += "RECENT ";
+ break;
+ }
+ }
+ }
+ if (forbiddenFlags != null) {
+ for (Flag f : forbiddenFlags) {
+ switch (f) {
+ case DELETED:
+ imapQuery += "UNDELETED ";
+ break;
+
+ case SEEN:
+ imapQuery += "UNSEEN ";
+ break;
+
+ case ANSWERED:
+ imapQuery += "UNANSWERED ";
+ break;
+
+ case FLAGGED:
+ imapQuery += "UNFLAGGED ";
+ break;
+
+ case DRAFT:
+ imapQuery += "UNDRAFT ";
+ break;
+
+ case RECENT:
+ imapQuery += "UNRECENT ";
+ break;
+ }
+ }
+ }
+ String encodedQry = encodeString(queryString);
+ if (mAccount.isRemoteSearchFullText()) {
+ imapQuery += "TEXT " + encodedQry;
+ } else {
+ imapQuery += "OR SUBJECT " + encodedQry + " FROM " + encodedQry;
+ }
+ return folder.executeSimpleCommand(imapQuery);
+ }
+ };
+
+ //don't pass listener--we don't want to add messages until we've downloaded them
+ return folder.search(searcher, null);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new MessagingException("Error during search: " + e.toString());
+ }
+
+ }
+
+
+
}
diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java
index 5d56aa24d..b055dbfda 100644
--- a/src/com/fsck/k9/mail/store/LocalStore.java
+++ b/src/com/fsck/k9/mail/store/LocalStore.java
@@ -1,7 +1,15 @@
package com.fsck.k9.mail.store;
-import java.io.*;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
@@ -17,7 +25,6 @@ import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
-import com.fsck.k9.helper.HtmlConverter;
import org.apache.commons.io.IOUtils;
import android.app.Application;
@@ -36,8 +43,10 @@ import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
import com.fsck.k9.Account.MessageFormat;
+import com.fsck.k9.activity.Search;
import com.fsck.k9.controller.MessageRemovalListener;
import com.fsck.k9.controller.MessageRetrievalListener;
+import com.fsck.k9.helper.HtmlConverter;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Body;
@@ -1127,9 +1136,15 @@ public class LocalStore extends Store implements Serializable {
@Override
public void open(final OpenMode mode) throws MessagingException {
- if (isOpen()) {
+
+ if (isOpen() && (getMode() == mode || mode == OpenMode.READ_ONLY)) {
return;
+ } else if (isOpen()) {
+ //previously opened in READ_ONLY and now requesting READ_WRITE
+ //so close connection and reopen
+ close();
}
+
try {
database.execute(false, new DbCallback() {
@Override
@@ -1336,17 +1351,19 @@ public class LocalStore extends Store implements Serializable {
}
public void purgeToVisibleLimit(MessageRemovalListener listener) throws MessagingException {
- if (mVisibleLimit == 0) {
- return ;
- }
- open(OpenMode.READ_WRITE);
- Message[] messages = getMessages(null, false);
- for (int i = mVisibleLimit; i < messages.length; i++) {
- if (listener != null) {
- listener.messageRemoved(messages[i]);
+ //don't purge messages while a Search is active since it might throw away search results
+ if (!Search.isActive()) {
+ if (mVisibleLimit == 0) {
+ return ;
+ }
+ open(OpenMode.READ_WRITE);
+ Message[] messages = getMessages(null, false);
+ for (int i = mVisibleLimit; i < messages.length; i++) {
+ if (listener != null) {
+ listener.messageRemoved(messages[i]);
+ }
+ messages[i].destroy();
}
- messages[i].destroy();
-
}
}
@@ -1831,11 +1848,11 @@ public class LocalStore extends Store implements Serializable {
}
@Override
- public Message getMessage(final String uid) throws MessagingException {
+ public LocalMessage getMessage(final String uid) throws MessagingException {
try {
- return database.execute(false, new DbCallback() {
+ return database.execute(false, new DbCallback() {
@Override
- public Message doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
+ public LocalMessage doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
try {
open(OpenMode.READ_WRITE);
LocalMessage message = new LocalMessage(uid, LocalFolder.this);
@@ -2110,7 +2127,7 @@ public class LocalStore extends Store implements Serializable {
/*
* Replace an existing message in the database
*/
- LocalMessage oldMessage = (LocalMessage) getMessage(uid);
+ LocalMessage oldMessage = getMessage(uid);
if (oldMessage != null) {
oldMessageId = oldMessage.getId();