diff --git a/app/src/main/java/eu/faircode/email/Core.java b/app/src/main/java/eu/faircode/email/Core.java index 5242f2c326..0146b280f6 100644 --- a/app/src/main/java/eu/faircode/email/Core.java +++ b/app/src/main/java/eu/faircode/email/Core.java @@ -68,6 +68,7 @@ import com.sun.mail.imap.protocol.FLAGS; import com.sun.mail.imap.protocol.FetchResponse; import com.sun.mail.imap.protocol.IMAPProtocol; import com.sun.mail.imap.protocol.UID; +import com.sun.mail.imap.protocol.UIDSet; import com.sun.mail.pop3.POP3Folder; import com.sun.mail.pop3.POP3Message; import com.sun.mail.pop3.POP3Store; @@ -4037,21 +4038,40 @@ class Core { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); boolean perform_expunge = prefs.getBoolean("perform_expunge", true); + boolean uid_expunge = prefs.getBoolean("uid_expunge", false); + if (!perform_expunge) return false; try { - boolean uidplus = MessageHelper.hasCapability(ifolder, "UIDPLUS"); - if (uidplus) { + if (uid_expunge) + uid_expunge = MessageHelper.hasCapability(ifolder, "UIDPLUS"); + + if (uid_expunge) { + FetchProfile fp = new FetchProfile(); + fp.add(UIDFolder.FetchProfileItem.UID); + ifolder.fetch(messages.toArray(new Message[0]), fp); + List uids = new ArrayList<>(); for (Message m : messages) try { - uids.add(ifolder.getUID(m)); - } catch (MessagingException ex) { - uids.add(-1L); + long uid = ifolder.getUID(m); + if (uid < 0) + continue; + uids.add(uid); + } catch (MessageRemovedException ex) { + Log.w(ex); } + Log.i(ifolder.getName() + " expunging " + TextUtils.join(",", uids)); - ifolder.expunge(messages.toArray(new Message[0])); + ifolder.doCommand(new IMAPFolder.ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol protocol) throws ProtocolException { + // https://datatracker.ietf.org/doc/html/rfc4315#section-2.1 + protocol.uidexpunge(UIDSet.createUIDSets(Helper.toLongArray(uids))); + return null; + } + }); Log.i(ifolder.getName() + " expunged " + TextUtils.join(",", uids)); } else { Log.i(ifolder.getName() + " expunging all"); diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java b/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java index 8a36900188..c5374232f7 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java @@ -140,6 +140,7 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc private ImageButton ibSqliteCache; private SwitchCompat swModSeq; private SwitchCompat swExpunge; + private SwitchCompat swUidExpunge; private SwitchCompat swAuthPlain; private SwitchCompat swAuthLogin; private SwitchCompat swAuthNtlm; @@ -177,7 +178,7 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc "experiments", "crash_reports", "cleanup_attachments", "protocol", "debug", "log_level", "query_threads", "wal", "checkpoints", "sqlite_cache", - "chunk_size", "use_modseq", "perform_expunge", + "chunk_size", "use_modseq", "perform_expunge", "uid_expunge", "auth_plain", "auth_login", "auth_ntlm", "auth_sasl", "idle_done", "exact_alarms", "dup_msgids", "test_iab" }; @@ -270,6 +271,7 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc sbChunkSize = view.findViewById(R.id.sbChunkSize); swModSeq = view.findViewById(R.id.swModSeq); swExpunge = view.findViewById(R.id.swExpunge); + swUidExpunge = view.findViewById(R.id.swUidExpunge); swAuthPlain = view.findViewById(R.id.swAuthPlain); swAuthLogin = view.findViewById(R.id.swAuthLogin); swAuthNtlm = view.findViewById(R.id.swAuthNtlm); @@ -823,6 +825,15 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { prefs.edit().putBoolean("perform_expunge", checked).apply(); + swUidExpunge.setEnabled(checked); + ServiceSynchronize.reload(compoundButton.getContext(), null, true, "perform_expunge"); + } + }); + + swUidExpunge.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + prefs.edit().putBoolean("uid_expunge", checked).apply(); ServiceSynchronize.reload(compoundButton.getContext(), null, true, "perform_expunge"); } }); @@ -1327,6 +1338,8 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc swModSeq.setChecked(prefs.getBoolean("use_modseq", true)); swExpunge.setChecked(prefs.getBoolean("perform_expunge", true)); + swUidExpunge.setChecked(prefs.getBoolean("uid_expunge", false)); + swUidExpunge.setEnabled(swExpunge.isChecked()); swAuthPlain.setChecked(prefs.getBoolean("auth_plain", true)); swAuthLogin.setChecked(prefs.getBoolean("auth_login", true)); swAuthNtlm.setChecked(prefs.getBoolean("auth_ntlm", true)); diff --git a/app/src/main/res/layout/fragment_options_misc.xml b/app/src/main/res/layout/fragment_options_misc.xml index 1749554c62..39765dac39 100644 --- a/app/src/main/res/layout/fragment_options_misc.xml +++ b/app/src/main/res/layout/fragment_options_misc.xml @@ -791,6 +791,18 @@ app:layout_constraintTop_toBottomOf="@id/swModSeq" app:switchPadding="12dp" /> + + Chunk size: %1$d MODSEQ AUTO EXPUNGE + UID EXPUNGE PLAIN LOGIN NTLM