Repository: thunderbird/thunderbird-android (language: kotlin)

Issue:
Crash when attempting to delete a draft message that has already been removed (by another client)
### Checklist

- [X] I have used the search function to see if someone else has already submitted the same bug report.
- [X] I will describe the problem with as much detail as possible.

### App version

6.309

### Where did you get the app from?

Google Play

### Android version

Android 12

### Device model

_No response_

### Steps to reproduce

1. Open a draft message in K-9 Mail
2. Delete the draft message from the server using another client. Make sure this change is synchronized to K-9 Mail while the compose screen is open (e.g. use Push for the Drafts folder)
3. Send the message in K-9 Mail

### Expected behavior

Don't crash

### Actual behavior

Crash

### Logs

Strack trace (via Google Play Developer Console):

```text
Exception java.lang.RuntimeException: An error occurred while executing doInBackground()
  at android.os.AsyncTask$4.done (AsyncTask.java:415)
  at java.util.concurrent.FutureTask.finishCompletion (FutureTask.java:381)
  at java.util.concurrent.FutureTask.setException (FutureTask.java:250)
  at java.util.concurrent.FutureTask.run (FutureTask.java:269)
  at android.os.AsyncTask$SerialExecutor$1.run (AsyncTask.java:305)
  at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1137)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:637)
  at java.lang.Thread.run (Thread.java:1012)
Caused by com.fsck.k9.mail.MessagingException:
  at com.fsck.k9.storage.messages.RetrieveMessageOperations.getMessageServerId$lambda$1 (RetrieveMessageOperations.kt:28)
  at com.fsck.k9.storage.messages.RetrieveMessageOperations.$r8$lambda$InoHsttbjEG6Xq4uHbyl8sOVdZQ
  at com.fsck.k9.storage.messages.RetrieveMessageOperations$$ExternalSyntheticLambda1.doDbWork
  at com.fsck.k9.mailstore.LockableDatabase.execute (LockableDatabase.java:169)
  at com.fsck.k9.storage.messages.RetrieveMessageOperations.getMessageServerId (RetrieveMessageOperations.kt:17)
  at com.fsck.k9.storage.messages.K9MessageStore.getMessageServerId (K9MessageStore.kt:82)
  at com.fsck.k9.mailstore.NotifierMessageStore.getMessageServerId
  at com.fsck.k9.mailstore.ListenableMessageStore.getMessageServerId
  at com.fsck.k9.controller.MessagingController.deleteDraft (MessagingController.java:1925)
  at com.fsck.k9.activity.MessageCompose$SendMessageTask.doInBackground (MessageCompose.java:1503)
  at com.fsck.k9.activity.MessageCompose$SendMessageTask.doInBackground (MessageCompose.java:1464)
  at android.os.AsyncTask$3.call (AsyncTask.java:394)
  at java.util.concurrent.FutureTask.run (FutureTask.java:264)
```


Discussion hints:
nan

Pull request: Don't throw when calling `MessageStore.getMessageServerId()`
Return `null` when the message can no longer be found in the message store.

Fixes #6432

Test command: ./gradlew :app:storage:testDebugUnitTest --tests "com.fsck.k9.storage.messages.RetrieveMessageOperationsTest"

Fix patch:
diff --git a/app/core/src/main/java/com/fsck/k9/controller/DraftOperations.kt b/app/core/src/main/java/com/fsck/k9/controller/DraftOperations.kt
index b613b650e63..83963f052d2 100644
--- a/app/core/src/main/java/com/fsck/k9/controller/DraftOperations.kt
+++ b/app/core/src/main/java/com/fsck/k9/controller/DraftOperations.kt
@@ -75,8 +75,10 @@ internal class DraftOperations(
             messagingController.queuePendingCommand(account, command)
         } else {
             val fakeMessageServerId = messageStore.getMessageServerId(messageId)
-            val command = PendingAppend.create(folderId, fakeMessageServerId)
-            messagingController.queuePendingCommand(account, command)
+            if (fakeMessageServerId != null) {
+                val command = PendingAppend.create(folderId, fakeMessageServerId)
+                messagingController.queuePendingCommand(account, command)
+            }
         }
 
         messagingController.processPendingCommands(account)
diff --git a/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java b/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java
index 0d9ed85cf1b..8d503e35d2a 100644
--- a/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java
+++ b/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java
@@ -1615,9 +1615,11 @@ private void moveOrDeleteSentMessage(Account account, LocalStore localStore, Loc
 
             if (!sentFolder.isLocalOnly()) {
                 String destinationUid = messageStore.getMessageServerId(destinationMessageId);
-                PendingCommand command = PendingAppend.create(sentFolderId, destinationUid);
-                queuePendingCommand(account, command);
-                processPendingCommands(account);
+                if (destinationUid != null) {
+                    PendingCommand command = PendingAppend.create(sentFolderId, destinationUid);
+                    queuePendingCommand(account, command);
+                    processPendingCommands(account);
+                }
             }
         }
 
@@ -1901,9 +1903,10 @@ public void deleteDraft(Account account, long messageId) {
 
         MessageStore messageStore = messageStoreManager.getMessageStore(account);
         String messageServerId = messageStore.getMessageServerId(messageId);
-        MessageReference messageReference = new MessageReference(account.getUuid(), folderId, messageServerId);
-
-        deleteMessage(messageReference);
+        if (messageServerId != null) {
+            MessageReference messageReference = new MessageReference(account.getUuid(), folderId, messageServerId);
+            deleteMessage(messageReference);
+        }
     }
 
     public void deleteThreads(final List<MessageReference> messages) {
diff --git a/app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt b/app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt
index 724f78083a1..75c292e237e 100644
--- a/app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt
+++ b/app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt
@@ -91,7 +91,7 @@ interface MessageStore {
     /**
      * Retrieve the server ID for a given message.
      */
-    fun getMessageServerId(messageId: Long): String
+    fun getMessageServerId(messageId: Long): String?
 
     /**
      * Retrieve the server IDs for the given messages.
diff --git a/app/storage/src/main/java/com/fsck/k9/storage/messages/K9MessageStore.kt b/app/storage/src/main/java/com/fsck/k9/storage/messages/K9MessageStore.kt
index ee69f27d90a..ca1e7dec6ab 100644
--- a/app/storage/src/main/java/com/fsck/k9/storage/messages/K9MessageStore.kt
+++ b/app/storage/src/main/java/com/fsck/k9/storage/messages/K9MessageStore.kt
@@ -78,7 +78,7 @@ class K9MessageStore(
         updateMessageOperations.clearNewMessageState()
     }
 
-    override fun getMessageServerId(messageId: Long): String {
+    override fun getMessageServerId(messageId: Long): String? {
         return retrieveMessageOperations.getMessageServerId(messageId)
     }
 
diff --git a/app/storage/src/main/java/com/fsck/k9/storage/messages/RetrieveMessageOperations.kt b/app/storage/src/main/java/com/fsck/k9/storage/messages/RetrieveMessageOperations.kt
index a57bfee5411..c094ae3b3c2 100644
--- a/app/storage/src/main/java/com/fsck/k9/storage/messages/RetrieveMessageOperations.kt
+++ b/app/storage/src/main/java/com/fsck/k9/storage/messages/RetrieveMessageOperations.kt
@@ -4,7 +4,6 @@ import androidx.core.database.getLongOrNull
 import com.fsck.k9.K9
 import com.fsck.k9.mail.Flag
 import com.fsck.k9.mail.Header
-import com.fsck.k9.mail.MessagingException
 import com.fsck.k9.mail.internet.MimeHeader
 import com.fsck.k9.mail.message.MessageHeaderParser
 import com.fsck.k9.mailstore.LockableDatabase
@@ -13,7 +12,7 @@ import java.util.Date
 
 internal class RetrieveMessageOperations(private val lockableDatabase: LockableDatabase) {
 
-    fun getMessageServerId(messageId: Long): String {
+    fun getMessageServerId(messageId: Long): String? {
         return lockableDatabase.execute(false) { database ->
             database.query(
                 "messages",
@@ -25,7 +24,7 @@ internal class RetrieveMessageOperations(private val lockableDatabase: LockableD
                 if (cursor.moveToFirst()) {
                     cursor.getString(0)
                 } else {
-                    throw MessagingException("Message [ID: $messageId] not found in database")
+                    null
                 }
             }
         }


Test patch:
diff --git a/app/storage/src/test/java/com/fsck/k9/storage/messages/RetrieveMessageOperationsTest.kt b/app/storage/src/test/java/com/fsck/k9/storage/messages/RetrieveMessageOperationsTest.kt
index 5825dccec9d..09dccb31d70 100644
--- a/app/storage/src/test/java/com/fsck/k9/storage/messages/RetrieveMessageOperationsTest.kt
+++ b/app/storage/src/test/java/com/fsck/k9/storage/messages/RetrieveMessageOperationsTest.kt
@@ -2,7 +2,6 @@ package com.fsck.k9.storage.messages
 
 import com.fsck.k9.mail.Flag
 import com.fsck.k9.mail.Header
-import com.fsck.k9.mail.MessagingException
 import com.fsck.k9.mail.crlf
 import com.fsck.k9.storage.RobolectricTest
 import com.google.common.truth.Truth.assertThat
@@ -14,9 +13,11 @@ class RetrieveMessageOperationsTest : RobolectricTest() {
     private val lockableDatabase = createLockableDatabaseMock(sqliteDatabase)
     private val retrieveMessageOperations = RetrieveMessageOperations(lockableDatabase)
 
-    @Test(expected = MessagingException::class)
+    @Test
     fun `get message server id of non-existent message`() {
-        retrieveMessageOperations.getMessageServerId(42)
+        val messageServerId = retrieveMessageOperations.getMessageServerId(42)
+
+        assertThat(messageServerId).isNull()
     }
 
     @Test