1
0
mirror of https://github.com/moparisthebest/k-9 synced 2025-01-07 11:48:07 -05:00

Add spam filtering capability to MessagingController

This commit is contained in:
cketti 2013-01-13 12:44:16 +01:00
parent d2d7d858db
commit 6c81fc5261
4 changed files with 67 additions and 7 deletions

View File

@ -294,6 +294,8 @@ public class K9 extends Application {
public static final String IDENTITY_HEADER = "X-K9mail-Identity"; public static final String IDENTITY_HEADER = "X-K9mail-Identity";
public static final String SPAM_FLAG_HEADER = "X-Spam-Flag";
/** /**
* Specifies how many messages will be shown in a folder by default. This number is set * Specifies how many messages will be shown in a folder by default. This number is set
* on each new folder and can be incremented with "Load more messages..." by the * on each new folder and can be incremented with "Load more messages..." by the

View File

@ -1256,6 +1256,7 @@ public class MessagingController implements Runnable {
messages.clear(); messages.clear();
final ArrayList<Message> largeMessages = new ArrayList<Message>(); final ArrayList<Message> largeMessages = new ArrayList<Message>();
final ArrayList<Message> smallMessages = new ArrayList<Message>(); final ArrayList<Message> smallMessages = new ArrayList<Message>();
final Map<String, String> spamMessages = new HashMap<String, String>();
if (!unsyncedMessages.isEmpty()) { if (!unsyncedMessages.isEmpty()) {
/* /*
@ -1280,7 +1281,33 @@ public class MessagingController implements Runnable {
Log.d(K9.LOG_TAG, "SYNC: About to fetch " + unsyncedMessages.size() + " unsynced messages for folder " + folder); Log.d(K9.LOG_TAG, "SYNC: About to fetch " + unsyncedMessages.size() + " unsynced messages for folder " + folder);
fetchUnsyncedMessages(account, remoteFolder, localFolder, unsyncedMessages, smallMessages, largeMessages, progress, todo, fp); fetchUnsyncedMessages(account, remoteFolder, localFolder, unsyncedMessages, smallMessages, largeMessages, spamMessages, progress, todo, fp);
if (spamMessages.size() > 0) {
if (K9.DEBUG) {
Log.d(K9.LOG_TAG, "Moving " + spamMessages.size() +
" messages to the Spam folder.");
}
queueMoveOrCopy(account, localFolder.getName(), account.getSpamFolderName(), false,
spamMessages.keySet().toArray(EMPTY_STRING_ARRAY), spamMessages);
spamMessages.clear();
try {
processPendingCommandsSynchronous(account);
} catch (Exception e) {
addErrorMessage(account, null, e);
Log.e(K9.LOG_TAG, "Failure processing command, but continuing message sync attempt", e);
}
// FIXME: This is bad!
// ImapStore is using a cache for ImapFolder instances which causes our remoteFolder
// object to be used and later closed by processPendingMoveOrCopy(). So we have to
// re-open it here.
if (!remoteFolder.isOpen()) {
remoteFolder.open(OpenMode.READ_WRITE);
}
}
// If a message didn't exist, messageFinished won't be called, but we shouldn't try again // If a message didn't exist, messageFinished won't be called, but we shouldn't try again
// If we got here, nothing failed // If we got here, nothing failed
@ -1433,6 +1460,7 @@ public class MessagingController implements Runnable {
List<Message> unsyncedMessages, List<Message> unsyncedMessages,
final ArrayList<Message> smallMessages, final ArrayList<Message> smallMessages,
final ArrayList<Message> largeMessages, final ArrayList<Message> largeMessages,
final Map<String, String> spamMessages,
final AtomicInteger progress, final AtomicInteger progress,
final int todo, final int todo,
FetchProfile fp) throws MessagingException { FetchProfile fp) throws MessagingException {
@ -1445,6 +1473,11 @@ public class MessagingController implements Runnable {
*/ */
final List<Message> chunk = new ArrayList<Message>(UNSYNC_CHUNK_SIZE); final List<Message> chunk = new ArrayList<Message>(UNSYNC_CHUNK_SIZE);
final String spamFolderName = account.getSpamFolderName();
final LocalFolder spamFolder = account.getLocalStore().getFolder(spamFolderName);
final boolean isSpamFilterEnabled = (account.isSpamAssassinFilterEnabled() &&
!spamFolder.equals(localFolder));
remoteFolder.fetch(unsyncedMessages.toArray(EMPTY_MESSAGE_ARRAY), fp, remoteFolder.fetch(unsyncedMessages.toArray(EMPTY_MESSAGE_ARRAY), fp,
new MessageRetrievalListener() { new MessageRetrievalListener() {
@Override @Override
@ -1472,6 +1505,15 @@ public class MessagingController implements Runnable {
return; return;
} }
if (isSpamFilterEnabled && isSpam(message)) {
// Write message to the local Spam folder
Map<String, String> uidMap = spamFolder.insertAsLocalMessages(
new Message[] { message });
spamMessages.putAll(uidMap);
return;
}
if (account.getMaximumAutoDownloadMessageSize() > 0 && if (account.getMaximumAutoDownloadMessageSize() > 0 &&
message.getSize() > account.getMaximumAutoDownloadMessageSize()) { message.getSize() > account.getMaximumAutoDownloadMessageSize()) {
largeMessages.add(message); largeMessages.add(message);
@ -1518,6 +1560,11 @@ public class MessagingController implements Runnable {
} }
} }
private boolean isSpam(Message message) throws MessagingException {
String[] headers = message.getHeader(K9.SPAM_FLAG_HEADER);
return (headers != null && headers.length > 0 && headers[0].equalsIgnoreCase("YES"));
}
/** /**
* Actual storing of messages * Actual storing of messages
* *

View File

@ -1492,7 +1492,8 @@ public class ImapStore extends Store {
fetchFields.add("INTERNALDATE"); fetchFields.add("INTERNALDATE");
fetchFields.add("RFC822.SIZE"); fetchFields.add("RFC822.SIZE");
fetchFields.add("BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc " + fetchFields.add("BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc " +
"reply-to message-id references in-reply-to " + K9.IDENTITY_HEADER + ")]"); "reply-to message-id references in-reply-to " + K9.SPAM_FLAG_HEADER +
" " + K9.IDENTITY_HEADER + ")]");
} }
if (fp.contains(FetchProfile.Item.STRUCTURE)) { if (fp.contains(FetchProfile.Item.STRUCTURE)) {
fetchFields.add("BODYSTRUCTURE"); fetchFields.add("BODYSTRUCTURE");

View File

@ -2130,7 +2130,7 @@ public class LocalStore extends Store implements Serializable {
if (!(folder instanceof LocalFolder)) { if (!(folder instanceof LocalFolder)) {
throw new MessagingException("copyMessages called with incorrect Folder"); throw new MessagingException("copyMessages called with incorrect Folder");
} }
return ((LocalFolder) folder).appendMessages(msgs, true); return ((LocalFolder) folder).appendMessages(msgs, true, false);
} }
@Override @Override
@ -2299,7 +2299,7 @@ public class LocalStore extends Store implements Serializable {
*/ */
@Override @Override
public Map<String, String> appendMessages(Message[] messages) throws MessagingException { public Map<String, String> appendMessages(Message[] messages) throws MessagingException {
return appendMessages(messages, false); return appendMessages(messages, false, false);
} }
public void destroyMessages(final Message[] messages) { public void destroyMessages(final Message[] messages) {
@ -2389,6 +2389,11 @@ public class LocalStore extends Store implements Serializable {
return null; return null;
} }
public Map<String, String> insertAsLocalMessages(Message[] messages)
throws MessagingException {
return appendMessages(messages, false, true);
}
/** /**
* The method differs slightly from the contract; If an incoming message already has a uid * The method differs slightly from the contract; If an incoming message already has a uid
* assigned and it matches the uid of an existing message then this message will replace * assigned and it matches the uid of an existing message then this message will replace
@ -2401,9 +2406,14 @@ public class LocalStore extends Store implements Serializable {
* message, retrieve the appropriate local message instance first (if it already exists). * message, retrieve the appropriate local message instance first (if it already exists).
* @param messages * @param messages
* @param copy * @param copy
* @param createAsLocal
* If {@code true} the message will get a local UID. The mapping is added to the map
* that is returned by this method.
*
* @return Map<String, String> uidMap of srcUids -> destUids * @return Map<String, String> uidMap of srcUids -> destUids
*/ */
private Map<String, String> appendMessages(final Message[] messages, final boolean copy) throws MessagingException { private Map<String, String> appendMessages(final Message[] messages, final boolean copy,
final boolean createAsLocal) throws MessagingException {
open(OpenMode.READ_WRITE); open(OpenMode.READ_WRITE);
try { try {
final Map<String, String> uidMap = new HashMap<String, String>(); final Map<String, String> uidMap = new HashMap<String, String>();
@ -2418,14 +2428,14 @@ public class LocalStore extends Store implements Serializable {
long oldMessageId = -1; long oldMessageId = -1;
String uid = message.getUid(); String uid = message.getUid();
if (uid == null || copy) { if (uid == null || copy || createAsLocal) {
/* /*
* Create a new message in the database * Create a new message in the database
*/ */
String randomLocalUid = K9.LOCAL_UID_PREFIX + String randomLocalUid = K9.LOCAL_UID_PREFIX +
UUID.randomUUID().toString(); UUID.randomUUID().toString();
if (copy) { if (copy || createAsLocal) {
// Save mapping: source UID -> target UID // Save mapping: source UID -> target UID
uidMap.put(uid, randomLocalUid); uidMap.put(uid, randomLocalUid);
} else { } else {