Repository: thundernest/k-9
Issue: Drafts deleted when syncronizing with IMAP server

Please search to check for an existing issue (including closed issues, for which the fix may not have yet been released) before opening a new issue: https://github.com/k9mail/k-9/issues?q=is%3Aissue

### Expected behavior
When "Save as Draft" is selected, draft should be remain saved either locally or on the server for future access. 

### Actual behavior
Either draft is automatically completely deleted and no longer available upon server sync, or the draft remains in the "drafts" folder but when opening the compose window, the text is no longer available. It seems this is dependent on the encryption setting; everytime I re-open the draft I have to re-select "Don't encrypt" because the default is always "encrypt if possible".  The last selection should be remembered. If "encypt if possible" or "encrypt" is selected, the draft is deleted upon sync.  If not, I can often no longer access the draft text. This happens regardless of account, and only started after upgrading to the latest K9 version and adding my keys to OpenKeychain (I was using a much older version of K9 prior to this, not sure which).

### Steps to reproduce
1. Create a draft email with text in the body.
2. Select "don't encrypt"
3. Save draft by selecting either "save draft" or by pressing back and "save draft".
4. Text is gone after server sync.

OR

1. Create a draft email with text in the body.
2. Select "encrypt" or "encrypt if possible".
3. Save draft by selecting either "save draft" or by pressing back and "save draft".
4. Draft is gone after server sync. 


### Environment
K-9 Mail version: 5.203

Android version: 5.1.1

Account type (IMAP, POP3, WebDAV/Exchange): IMAP


Changed files:
k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java, k9mail/src/main/java/com/fsck/k9/controller/imap/ImapSync.java

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 0f42badfa..718c6efe1 100644
--- a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java
+++ b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java
@@ -898,7 +898,7 @@ public class MessagingController {
             if (account.syncRemoteDeletions()) {
                 List<String> destroyMessageUids = new ArrayList<>();
                 for (String localMessageUid : localUidMap.keySet()) {
-                    if (remoteUidMap.get(localMessageUid) == null) {
+                    if (!localMessageUid.startsWith(K9.LOCAL_UID_PREFIX) && remoteUidMap.get(localMessageUid) == null) {
                         destroyMessageUids.add(localMessageUid);
                     }
                 }
@@ -1761,9 +1761,15 @@ public class MessagingController {
                 localMessage.setFlag(Flag.X_REMOTE_COPY_STARTED, true);
                 remoteFolder.appendMessages(Collections.singletonList(localMessage));
 
-                localFolder.changeUid(localMessage);
-                for (MessagingListener l : getListeners()) {
-                    l.messageUidChanged(account, folder, oldUid, localMessage.getUid());
+                if (localMessage.getUid().startsWith(K9.LOCAL_UID_PREFIX)) {
+                    // We didn't get the server UID of the uploaded message. Remove the local message now. The uploaded
+                    // version will be downloaded during the next sync.
+                    localFolder.destroyMessages(Collections.singletonList(localMessage));
+                } else {
+                    localFolder.changeUid(localMessage);
+                    for (MessagingListener l : getListeners()) {
+                        l.messageUidChanged(account, folder, oldUid, localMessage.getUid());
+                    }
                 }
             } else {
                 /*
@@ -1796,10 +1802,17 @@ public class MessagingController {
                     localMessage.setFlag(Flag.X_REMOTE_COPY_STARTED, true);
 
                     remoteFolder.appendMessages(Collections.singletonList(localMessage));
-                    localFolder.changeUid(localMessage);
-                    for (MessagingListener l : getListeners()) {
-                        l.messageUidChanged(account, folder, oldUid, localMessage.getUid());
+                    if (localMessage.getUid().startsWith(K9.LOCAL_UID_PREFIX)) {
+                        // We didn't get the server UID of the uploaded message. Remove the local message now. The
+                        // uploaded version will be downloaded during the next sync.
+                        localFolder.destroyMessages(Collections.singletonList(localMessage));
+                    } else {
+                        localFolder.changeUid(localMessage);
+                        for (MessagingListener l : getListeners()) {
+                            l.messageUidChanged(account, folder, oldUid, localMessage.getUid());
+                        }
                     }
+
                     if (remoteDate != null) {
                         remoteMessage.setFlag(Flag.DELETED, true);
                         if (Expunge.EXPUNGE_IMMEDIATELY == account.getExpungePolicy()) {
@@ -3172,9 +3185,8 @@ public class MessagingController {
     private void deleteMessagesSynchronous(final Account account, final String folder,
             final List<? extends Message> messages,
             MessagingListener listener) {
-        Folder localFolder = null;
-        Folder localTrashFolder = null;
-        List<String> uids = getUidsFromMessages(messages);
+        LocalFolder localFolder = null;
+        LocalFolder localTrashFolder = null;
         try {
             //We need to make these callbacks before moving the messages to the trash
             //as messages get a new UID after being moved
@@ -3183,13 +3195,32 @@ public class MessagingController {
                     l.messageDeleted(account, folder, message);
                 }
             }
-            Store localStore = account.getLocalStore();
+
+            List<Message> localOnlyMessages = new ArrayList<>();
+            List<Message> syncedMessages = new ArrayList<>();
+            List<String> syncedMessageUids = new ArrayList<>();
+            for (Message message : messages) {
+                String uid = message.getUid();
+                if (uid.startsWith(K9.LOCAL_UID_PREFIX)) {
+                    localOnlyMessages.add(message);
+                } else {
+                    syncedMessages.add(message);
+                    syncedMessageUids.add(uid);
+                }
+            }
+
+            LocalStore localStore = account.getLocalStore();
             localFolder = localStore.getFolder(folder);
             Map<String, String> uidMap = null;
             if (folder.equals(account.getTrashFolderName()) || !account.hasTrashFolder()) {
                 Timber.d("Deleting messages in trash folder or trash set to -None-, not copying");
 
-                localFolder.setFlags(messages, Collections.singleton(Flag.DELETED), true);
+                if (!localOnlyMessages.isEmpty()) {
+                    localFolder.destroyMessages(localOnlyMessages);
+                }
+                if (!syncedMessages.isEmpty()) {
+                    localFolder.setFlags(syncedMessages, Collections.singleton(Flag.DELETED), true);
+                }
             } else {
                 localTrashFolder = localStore.getFolder(account.getTrashFolderName());
                 if (!localTrashFolder.exists()) {
@@ -3221,18 +3252,21 @@ public class MessagingController {
                     queuePendingCommand(account, command);
                 }
                 processPendingCommands(account);
-            } else if (account.getDeletePolicy() == DeletePolicy.ON_DELETE) {
-                if (folder.equals(account.getTrashFolderName())) {
-                    queueSetFlag(account, folder, true, Flag.DELETED, uids);
+            } else if (!syncedMessageUids.isEmpty()) {
+                if (account.getDeletePolicy() == DeletePolicy.ON_DELETE) {
+                    if (folder.equals(account.getTrashFolderName())) {
+                        queueSetFlag(account, folder, true, Flag.DELETED, syncedMessageUids);
+                    } else {
+                        queueMoveOrCopy(account, folder, account.getTrashFolderName(), false,
+                                    syncedMessageUids, uidMap);
+                    }
+                    processPendingCommands(account);
+                } else if (account.getDeletePolicy() == DeletePolicy.MARK_AS_READ) {
+                    queueSetFlag(account, folder, true, Flag.SEEN, syncedMessageUids);
+                    processPendingCommands(account);
                 } else {
-                    queueMoveOrCopy(account, folder, account.getTrashFolderName(), false, uids, uidMap);
+                    Timber.d("Delete policy %s prevents delete from server", account.getDeletePolicy());
                 }
-                processPendingCommands(account);
-            } else if (account.getDeletePolicy() == DeletePolicy.MARK_AS_READ) {
-                queueSetFlag(account, folder, true, Flag.SEEN, uids);
-                processPendingCommands(account);
-            } else {
-                Timber.d("Delete policy %s prevents delete from server", account.getDeletePolicy());
             }
 
             unsuppressMessages(account, messages);
diff --git a/k9mail/src/main/java/com/fsck/k9/controller/imap/ImapSync.java b/k9mail/src/main/java/com/fsck/k9/controller/imap/ImapSync.java
index c16869c47..ebfa32872 100644
--- a/k9mail/src/main/java/com/fsck/k9/controller/imap/ImapSync.java
+++ b/k9mail/src/main/java/com/fsck/k9/controller/imap/ImapSync.java
@@ -227,7 +227,7 @@ class ImapSync {
             if (account.syncRemoteDeletions()) {
                 List<String> destroyMessageUids = new ArrayList<>();
                 for (String localMessageUid : localUidMap.keySet()) {
-                    if (remoteUidMap.get(localMessageUid) == null) {
+                    if (!localMessageUid.startsWith(K9.LOCAL_UID_PREFIX) && remoteUidMap.get(localMessageUid) == null) {
                         destroyMessageUids.add(localMessageUid);
                     }
                 }