diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/Folder.java b/k9mail-library/src/main/java/com/fsck/k9/mail/Folder.java index f15830120..10f0449c9 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/Folder.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/Folder.java @@ -116,6 +116,9 @@ public abstract class Folder { public void expunge() throws MessagingException {} + public void expungeUids(List uids) throws MessagingException { + } + /** * Populate a list of messages based upon a FetchProfile. See {@link FetchProfile} for the things that can * be fetched. diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/Capabilities.java b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/Capabilities.java index ee27bf032..a50a21d3b 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/Capabilities.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/Capabilities.java @@ -14,4 +14,5 @@ class Capabilities { public static final String COMPRESS_DEFLATE = "COMPRESS=DEFLATE"; public static final String STARTTLS = "STARTTLS"; public static final String SPECIAL_USE = "SPECIAL-USE"; + public static final String UID_PLUS = "UIDPLUS"; } diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/Commands.java b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/Commands.java index c1acd6631..c5137cae2 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/Commands.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/Commands.java @@ -18,4 +18,5 @@ class Commands { public static final String UID_STORE = "UID STORE"; public static final String UID_FETCH = "UID FETCH"; public static final String UID_COPY = "UID COPY"; + public static final String UID_EXPUNGE = "UID EXPUNGE"; } diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java index 627470685..75e1038ca 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java @@ -710,6 +710,10 @@ class ImapConnection { return capabilities.contains(Capabilities.IDLE); } + boolean isUidPlusCapable() { + return capabilities.contains(Capabilities.UID_PLUS); + } + public void close() { open = false; stacktraceForClose = new Exception(); diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapFolder.java b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapFolder.java index f62330dc8..14cec2a31 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapFolder.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapFolder.java @@ -1259,6 +1259,30 @@ class ImapFolder extends Folder { } } + @Override + public void expungeUids(List uids) throws MessagingException { + if (uids == null || uids.isEmpty()) { + throw new IllegalArgumentException("expungeUids() must be called with a non-empty set of UIDs"); + } + + open(OPEN_MODE_RW); + checkOpen(); + + try { + if (connection.isUidPlusCapable()) { + Set longUids = new HashSet<>(uids.size()); + for (String uid : uids) { + longUids.add(Long.parseLong(uid)); + } + connection.executeCommandWithIdSet(Commands.UID_EXPUNGE, "", longUids); + } else { + executeSimpleCommand("EXPUNGE"); + } + } catch (IOException ioe) { + throw ioExceptionHandler(connection, ioe); + } + } + @Override public void setFlags(Set flags, boolean value) throws MessagingException { open(OPEN_MODE_RW); diff --git a/k9mail-library/src/test/java/com/fsck/k9/mail/store/imap/ImapFolderTest.java b/k9mail-library/src/test/java/com/fsck/k9/mail/store/imap/ImapFolderTest.java index ebd2b1687..9b78a2b2a 100644 --- a/k9mail-library/src/test/java/com/fsck/k9/mail/store/imap/ImapFolderTest.java +++ b/k9mail-library/src/test/java/com/fsck/k9/mail/store/imap/ImapFolderTest.java @@ -1005,6 +1005,28 @@ public class ImapFolderTest { verify(imapConnection).executeSimpleCommand("EXPUNGE"); } + @Test + public void expungeUids_withUidPlus_shouldIssueUidExpungeCommand() throws Exception { + ImapFolder folder = createFolder("Folder"); + prepareImapFolderForOpen(OPEN_MODE_RW); + when(imapConnection.isUidPlusCapable()).thenReturn(true); + + folder.expungeUids(singletonList("1")); + + assertCommandWithIdsIssued("UID EXPUNGE 1"); + } + + @Test + public void expungeUids_withoutUidPlus_shouldIssueExpungeCommand() throws Exception { + ImapFolder folder = createFolder("Folder"); + prepareImapFolderForOpen(OPEN_MODE_RW); + when(imapConnection.isUidPlusCapable()).thenReturn(false); + + folder.expungeUids(singletonList("1")); + + verify(imapConnection).executeSimpleCommand("EXPUNGE"); + } + @Test public void setFlags_shouldIssueUidStoreCommand() throws Exception { ImapFolder folder = createFolder("Folder"); @@ -1226,8 +1248,8 @@ public class ImapFolderTest { for (int i = 0, end = commandPrefixes.size(); i < end; i++) { String command = commandPrefixes.get(i) + - " " + ImapUtility.join(",", commandUids.get(i)) + " " + - commandSuffixes.get(i); + " " + ImapUtility.join(",", commandUids.get(i)) + + ((commandSuffixes.get(i).length() == 0) ? "" : " " + commandSuffixes.get(i)); if (command.equals(expectedCommand)) { return; } diff --git a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java index 26cfd8e5e..acedcf0d1 100644 --- a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java +++ b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java @@ -1830,7 +1830,7 @@ public class MessagingController { if (remoteDate != null) { remoteMessage.setFlag(Flag.DELETED, true); if (Expunge.EXPUNGE_IMMEDIATELY == account.getExpungePolicy()) { - remoteFolder.expunge(); + remoteFolder.expungeUids(Collections.singletonList(remoteMessage.getUid())); } } } @@ -1914,7 +1914,11 @@ public class MessagingController { } if (!isCopy && Expunge.EXPUNGE_IMMEDIATELY == account.getExpungePolicy()) { Timber.i("processingPendingMoveOrCopy expunging folder %s:%s", account.getDescription(), srcFolder); - remoteSrcFolder.expunge(); + List movedUids = new ArrayList<>(messages.size()); + for (Message message : messages) { + movedUids.add(message.getUid()); + } + remoteSrcFolder.expungeUids(movedUids); } /*