Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite IMAP expunge #2927

Merged
merged 1 commit into from Nov 12, 2017
Merged

Rewrite IMAP expunge #2927

merged 1 commit into from Nov 12, 2017

Conversation

rhari991
Copy link

This PR rewrites the expunge() method in ImapFolder to ensure that only messages that were marked with the \Deleted flag by K-9 Mail are expunged. This process is described in section 4.2.4 of RFC 4549

Same as #2701

@@ -998,6 +998,42 @@ public LocalMessage doDbWork(final SQLiteDatabase db) throws WrappedException, U
}
}

public Set<Long> getDeletedMessageUids() throws MessagingException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very similar to the method above. It should be possible to use one underlying method for most of this and just passing in a different query.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also grumpy that there's no test, but there's no test for any LocalFolder stuff. Maybe I should look at that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philipwhiuk done !

@@ -693,7 +693,7 @@ public void synchronizeMailboxSynchronous_withAccountPolicySetToExpungeOnPoll_sh

controller.synchronizeMailboxSynchronous(account, FOLDER_NAME, listener, null);

verify(remoteFolder).expunge();
verify(remoteFolder).expunge(anySet());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

anySet() is pretty weak. Can't we verify we pass in the right IDs?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -113,7 +113,7 @@ public abstract void setFlags(List<? extends Message> messages, Set<Flag> flags,

public abstract String getUidFromMessageId(Message message) throws MessagingException;

public void expunge() throws MessagingException
public void expunge(List<Long> knownDeletedUids) throws MessagingException
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the Folder abstraction UIDs are opaque strings. UIDs being numbers is an implementation detail of ImapFolder. So until we can replace this interface with something more flexible let's keep this interface clean.

Also, sometimes (e.g. when emptying the Trash folder) an unconstrained EXPUNGE makes sense. So I'd keep the original method and add a new one that better describes what it is about. My suggestion:

public void expungeUids(List<String> uids) throws MessagingException

} catch (IOException ioe) {
throw ioExceptionHandler(connection, ioe);
}
}

private void expungeWithoutUidPlus(Set<Long> knownDeletedUids) throws IOException, MessagingException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is what RFC 4549 suggests when UID EXPUNGE is not available, but I don't particularly like it. It has the potential to undelete messages when the connection is lost in the middle of the command sequence. It also requires passing all known deleted UIDs to the method.

By now UIDPLUS and with that UID EXPUNGE should be widely available. So I'm fine with K-9 Mail not making the best effort when using legacy servers. Also, most users don't care about messages marked as deleted. So I'd say fall back to a plain EXPUNGE when UID EXPUNGE is not available. That is what we're doing right now, anyway.

@@ -1248,17 +1248,40 @@ public String getUidFromMessageId(Message message) throws MessagingException {
}

@Override
public void expunge() throws MessagingException {
public void expunge(List<Long> knownDeletedUids) throws MessagingException {
if (knownDeletedUids == null || knownDeletedUids.isEmpty()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both cases are programming errors. So I'd rather throw an exception than returning silently.


private void setFlagsInternal(Set<Long> uids, final Set<Flag> flags, boolean value)
throws MessagingException {
if (uids.isEmpty()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this should never happen. If it does we want to know about it, so throw an exception.

@@ -1228,7 +1256,7 @@ private void assertCommandWithIdsIssued(String expectedCommand) throws Messaging
String command = commandPrefixes.get(i) +
" " + ImapUtility.join(",", commandUids.get(i)) + " " +
commandSuffixes.get(i);
if (command.equals(expectedCommand)) {
if (command.trim().equals(expectedCommand)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather fix the construction of command above than trimming the erroneously added space at the end.

@@ -803,7 +803,7 @@ Upload any local messages that are marked as PENDING_UPLOAD (Drafts, Sent, Trash

if (Expunge.EXPUNGE_ON_POLL == account.getExpungePolicy()) {
Timber.d("SYNC: Expunging folder %s:%s", account.getDescription(), folder);
remoteFolder.expunge();
remoteFolder.expunge(localFolder.getAllMessageUids(true));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually I want to get rid of 'expunge on sync'. For now I think preserving the current behavior by calling expunge() rather than expungeUids() is fine.

@@ -1830,7 +1830,7 @@ void processPendingAppend(PendingAppend command, Account account) throws Messagi
if (remoteDate != null) {
remoteMessage.setFlag(Flag.DELETED, true);
if (Expunge.EXPUNGE_IMMEDIATELY == account.getExpungePolicy()) {
remoteFolder.expunge();
remoteFolder.expunge(localFolder.getAllMessageUids(true));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With my suggestions applied this should only pass the UID of the current message to expungeUids().

@@ -1914,7 +1916,7 @@ void processPendingMoveOrCopy(PendingMoveOrCopy command, Account account) throws
}
if (!isCopy && Expunge.EXPUNGE_IMMEDIATELY == account.getExpungePolicy()) {
Timber.i("processingPendingMoveOrCopy expunging folder %s:%s", account.getDescription(), srcFolder);
remoteSrcFolder.expunge();
remoteSrcFolder.expunge(localSrcFolder.getAllMessageUids(true));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With my suggestions applied this should only pass the UIDs of the moved messages to expungeUids().

@@ -2020,10 +2026,11 @@ void processPendingExpunge(PendingExpunge command, Account account) throws Messa
if (remoteFolder.getMode() != Folder.OPEN_MODE_RW) {
return;
}
remoteFolder.expunge();
remoteFolder.expunge(localFolder.getAllMessageUids(true));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a manually triggered expunge and should therefore unconditionally expunge messages, i.e. call expunge() without arguments.

try {
if (remoteFolder.exists()) {
remoteFolder.open(Folder.OPEN_MODE_RW);
remoteFolder.setFlags(Collections.singleton(Flag.DELETED), true);
if (Expunge.EXPUNGE_IMMEDIATELY == account.getExpungePolicy()) {
remoteFolder.expunge();
remoteFolder.expunge(localFolder.getAllMessageUids(true));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, this should be expunge() without arguments.

@rhari991
Copy link
Author

@cketti Added the changes

@cketti
Copy link
Member

cketti commented Nov 12, 2017

👍

@philipwhiuk: Are you satisfied with the changes as well?

@rhari991 rhari991 merged commit 65be2b0 into master Nov 12, 2017
@rhari991 rhari991 deleted the rewrite-expunge branch November 12, 2017 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants