Repository: thundernest/k-9
Issue: S/MIME signed emails in sent folder misleadingly shown as PGP encrypted mails

### Expected behavior

I am using S/MIME to sign e-mails on my computer. When I open sent mails in k9 mail app on my smartphone the mail body is shown correctly, I can read the sent message, because it is just signed. I also see the attachment (signature file" smime.p7s") there.


### Actual behavior

Since last update the S/MIME signed mails are shown correctly in AutoPreview (I see text there), but after clicking on them to open the mail and read the full message. Instead a message "this mail is encrypted / This e-mail is with OpenPGP encrypted. To read it you need compatible OpenPGP-App installed and configured / Choose OpenPGPG app" appeaers and I have no chance to read the message.

### Steps to reproduce
1. use a s/mime to sign e-mails from your computer
2. open the sent mail on your smartphone with k9 mail app
3. then this message appears

### Environment
K-9 Mail version: 5.300 (installed 11th Sept. 2017)

Android version: 7.0

Account type (IMAP, POP3, WebDAV/Exchange): IMAP

Changed files:
k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java, k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java, k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoAnnotations.java, k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoSplitter.java, k9mail/src/main/java/com/fsck/k9/crypto/MessageCryptoStructureDetector.java, k9mail/src/test/java/com/fsck/k9/message/TestMessageConstructionUtils.java, k9mail/src/test/java/com/fsck/k9/mailstore/MessageViewInfoExtractorTest.java

diff --git a/k9mail/src/main/java/com/fsck/k9/crypto/MessageCryptoStructureDetector.java b/k9mail/src/main/java/com/fsck/k9/crypto/MessageCryptoStructureDetector.java
index 974811957..cd8bd7034 100644
--- a/k9mail/src/main/java/com/fsck/k9/crypto/MessageCryptoStructureDetector.java
+++ b/k9mail/src/main/java/com/fsck/k9/crypto/MessageCryptoStructureDetector.java
@@ -233,7 +233,7 @@ public class MessageCryptoStructureDetector {
         return dataUnavailable || protocolMatches;
     }
 
-    private static boolean isPartMultipartEncrypted(Part part) {
+    public static boolean isPartMultipartEncrypted(Part part) {
         if (!isSameMimeType(part.getMimeType(), MULTIPART_ENCRYPTED)) {
             return false;
         }
diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java b/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java
index f89ff127c..9934447f9 100644
--- a/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java
+++ b/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java
@@ -1,6 +1,7 @@
 package com.fsck.k9.mailstore;
 
 
+import java.util.Collections;
 import java.util.List;
 
 import com.fsck.k9.mail.Message;
@@ -36,24 +37,24 @@ public class MessageViewInfo {
         this.extraAttachments = extraAttachments;
     }
 
-    public static MessageViewInfo createWithExtractedContent(
-            Message message, boolean isMessageIncomplete, Part rootPart,
-            String text, List<AttachmentViewInfo> attachments,
-            CryptoResultAnnotation cryptoResultAnnotation,
-            AttachmentResolver attachmentResolver,
-            String extraText, List<AttachmentViewInfo> extraAttachments
-    ) {
+    static MessageViewInfo createWithExtractedContent(Message message, boolean isMessageIncomplete,
+            String text, List<AttachmentViewInfo> attachments, AttachmentResolver attachmentResolver) {
         return new MessageViewInfo(
-                message, isMessageIncomplete, rootPart,
-                text, attachments,
-                cryptoResultAnnotation,
-                attachmentResolver,
-                extraText, extraAttachments
-        );
+                message, isMessageIncomplete, message, text, attachments, null, attachmentResolver, null,
+                Collections.<AttachmentViewInfo>emptyList());
     }
 
     public static MessageViewInfo createWithErrorState(Message message, boolean isMessageIncomplete) {
         return new MessageViewInfo(message, isMessageIncomplete, null, null, null, null, null, null, null);
     }
 
+    MessageViewInfo withCryptoData(CryptoResultAnnotation rootPartAnnotation, String extraViewableText,
+            List<AttachmentViewInfo> extraAttachmentInfos) {
+        return new MessageViewInfo(
+                message, isMessageIncomplete, rootPart, text, attachments,
+                rootPartAnnotation,
+                attachmentResolver,
+                extraViewableText, extraAttachmentInfos
+        );
+    }
 }
diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java b/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java
index 8d318ca69..feeb06946 100644
--- a/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java
+++ b/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfoExtractor.java
@@ -12,22 +12,24 @@ import android.support.annotation.VisibleForTesting;
 import android.support.annotation.WorkerThread;
 
 import com.fsck.k9.Globals;
+import com.fsck.k9.K9;
 import com.fsck.k9.R;
+import com.fsck.k9.crypto.MessageCryptoStructureDetector;
 import com.fsck.k9.mail.Address;
 import com.fsck.k9.mail.Flag;
 import com.fsck.k9.mail.Message;
 import com.fsck.k9.mail.MessagingException;
 import com.fsck.k9.mail.Part;
 import com.fsck.k9.mail.internet.MessageExtractor;
+import com.fsck.k9.mail.internet.MimeUtility;
 import com.fsck.k9.mail.internet.Viewable;
 import com.fsck.k9.mail.internet.Viewable.Flowed;
+import com.fsck.k9.mailstore.CryptoResultAnnotation.CryptoError;
 import com.fsck.k9.mailstore.util.FlowedMessageUtils;
 import com.fsck.k9.message.extractors.AttachmentInfoExtractor;
 import com.fsck.k9.message.html.HtmlConverter;
 import com.fsck.k9.message.html.HtmlProcessor;
 import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
-import com.fsck.k9.ui.crypto.MessageCryptoSplitter;
-import com.fsck.k9.ui.crypto.MessageCryptoSplitter.CryptoMessageParts;
 import org.openintents.openpgp.util.OpenPgpUtils;
 import timber.log.Timber;
 
@@ -69,45 +71,61 @@ public class MessageViewInfoExtractor {
     }
 
     @WorkerThread
-    public MessageViewInfo extractMessageForView(Message message, @Nullable MessageCryptoAnnotations annotations)
+    public MessageViewInfo extractMessageForView(Message message, @Nullable MessageCryptoAnnotations cryptoAnnotations)
             throws MessagingException {
-        Part rootPart;
-        CryptoResultAnnotation cryptoResultAnnotation;
-        List<Part> extraParts;
-
-        CryptoMessageParts cryptoMessageParts = MessageCryptoSplitter.split(message, annotations);
-        if (cryptoMessageParts != null) {
-            rootPart = cryptoMessageParts.contentPart;
-            cryptoResultAnnotation = cryptoMessageParts.contentCryptoAnnotation;
-            extraParts = cryptoMessageParts.extraParts;
-        } else {
-            if (annotations != null && !annotations.isEmpty()) {
-                Timber.e("Got message annotations but no crypto root part!");
+        ArrayList<Part> extraParts = new ArrayList<>();
+        Part cryptoContentPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, extraParts);
+
+        if (cryptoContentPart == null) {
+            if (cryptoAnnotations != null && !cryptoAnnotations.isEmpty()) {
+                Timber.e("Got crypto message cryptoContentAnnotations but no crypto root part!");
             }
-            rootPart = message;
-            cryptoResultAnnotation = null;
-            extraParts = null;
+            return extractSimpleMessageForView(message, message);
         }
 
-        List<AttachmentViewInfo> attachmentInfos = new ArrayList<>();
-        ViewableExtractedText viewable = extractViewableAndAttachments(
-                Collections.singletonList(rootPart), attachmentInfos);
+        boolean isOpenPgpEncrypted = (MessageCryptoStructureDetector.isPartMultipartEncrypted(cryptoContentPart) &&
+                        MessageCryptoStructureDetector.isMultipartEncryptedOpenPgpProtocol(cryptoContentPart)) ||
+                        MessageCryptoStructureDetector.isPartPgpInlineEncrypted(cryptoContentPart);
+        if (!K9.isOpenPgpProviderConfigured() && isOpenPgpEncrypted) {
+            CryptoResultAnnotation noProviderAnnotation = CryptoResultAnnotation.createErrorAnnotation(
+                    CryptoError.OPENPGP_ENCRYPTED_NO_PROVIDER, null);
+            return MessageViewInfo.createWithErrorState(message, false)
+                    .withCryptoData(noProviderAnnotation, null, null);
+        }
 
-        List<AttachmentViewInfo> extraAttachmentInfos = new ArrayList<>();
-        String extraViewableText = null;
-        if (extraParts != null) {
-            ViewableExtractedText extraViewable =
-                    extractViewableAndAttachments(extraParts, extraAttachmentInfos);
-            extraViewableText = extraViewable.text;
+        CryptoResultAnnotation cryptoContentPartAnnotation =
+                cryptoAnnotations != null ? cryptoAnnotations.get(cryptoContentPart) : null;
+        if (cryptoContentPartAnnotation != null) {
+            return extractCryptoMessageForView(message, extraParts, cryptoContentPart, cryptoContentPartAnnotation);
+        }
+
+        return extractSimpleMessageForView(message, message);
+    }
+
+    private MessageViewInfo extractCryptoMessageForView(Message message,
+            ArrayList<Part> extraParts, Part cryptoContentPart, CryptoResultAnnotation cryptoContentPartAnnotation)
+            throws MessagingException {
+        if (cryptoContentPartAnnotation != null && cryptoContentPartAnnotation.hasReplacementData()) {
+            cryptoContentPart = cryptoContentPartAnnotation.getReplacementData();
         }
 
-        AttachmentResolver attachmentResolver = AttachmentResolver.createFromPart(rootPart);
+        List<AttachmentViewInfo> extraAttachmentInfos = new ArrayList<>();
+        ViewableExtractedText extraViewable = extractViewableAndAttachments(extraParts, extraAttachmentInfos);
+
+        MessageViewInfo messageViewInfo = extractSimpleMessageForView(message, cryptoContentPart);
+        return messageViewInfo.withCryptoData(cryptoContentPartAnnotation, extraViewable.text, extraAttachmentInfos);
+    }
 
-        boolean isMessageIncomplete = !message.isSet(Flag.X_DOWNLOADED_FULL) ||
-                MessageExtractor.hasMissingParts(message);
+    private MessageViewInfo extractSimpleMessageForView(Message message, Part contentPart) throws MessagingException {
+        List<AttachmentViewInfo> attachmentInfos = new ArrayList<>();
+        ViewableExtractedText viewable = extractViewableAndAttachments(
+                Collections.singletonList(contentPart), attachmentInfos);
+        AttachmentResolver attachmentResolver = AttachmentResolver.createFromPart(contentPart);
+        boolean isMessageIncomplete =
+                !message.isSet(Flag.X_DOWNLOADED_FULL) || MessageExtractor.hasMissingParts(message);
 
-        return MessageViewInfo.createWithExtractedContent(message, isMessageIncomplete, rootPart, viewable.html,
-                attachmentInfos, cryptoResultAnnotation, attachmentResolver, extraViewableText, extraAttachmentInfos);
+        return MessageViewInfo.createWithExtractedContent(
+                message, isMessageIncomplete, viewable.html, attachmentInfos, attachmentResolver);
     }
 
     private ViewableExtractedText extractViewableAndAttachments(List<Part> parts,
diff --git a/k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoAnnotations.java b/k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoAnnotations.java
index d0e58f129..a450b28d4 100644
--- a/k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoAnnotations.java
+++ b/k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoAnnotations.java
@@ -12,11 +12,7 @@ import com.fsck.k9.mailstore.CryptoResultAnnotation;
 public class MessageCryptoAnnotations {
     private HashMap<Part, CryptoResultAnnotation> annotations = new HashMap<>();
 
-    MessageCryptoAnnotations() {
-        // Package-private constructor
-    }
-
-    void put(Part part, CryptoResultAnnotation annotation) {
+    public void put(Part part, CryptoResultAnnotation annotation) {
         annotations.put(part, annotation);
     }
 
diff --git a/k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoSplitter.java b/k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoSplitter.java
deleted file mode 100644
index 02c439eff..000000000
--- a/k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoSplitter.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.fsck.k9.ui.crypto;
-
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import com.fsck.k9.crypto.MessageCryptoStructureDetector;
-import com.fsck.k9.mail.Message;
-import com.fsck.k9.mail.Part;
-import com.fsck.k9.mailstore.CryptoResultAnnotation;
-import com.fsck.k9.mailstore.CryptoResultAnnotation.CryptoError;
-
-
-public class MessageCryptoSplitter {
-    private MessageCryptoSplitter() { }
-
-    @Nullable
-    public static CryptoMessageParts split(@NonNull Message message, @Nullable MessageCryptoAnnotations annotations) {
-        ArrayList<Part> extraParts = new ArrayList<>();
-        Part primaryPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, extraParts);
-        if (primaryPart == null) {
-            return null;
-        }
-
-        if (annotations == null) {
-            CryptoResultAnnotation rootPartAnnotation =
-                    CryptoResultAnnotation.createErrorAnnotation(CryptoError.OPENPGP_ENCRYPTED_NO_PROVIDER, null);
-            return new CryptoMessageParts(primaryPart, rootPartAnnotation, extraParts);
-        }
-
-        CryptoResultAnnotation rootPartAnnotation = annotations.get(primaryPart);
-        Part rootPart;
-        if (rootPartAnnotation != null && rootPartAnnotation.hasReplacementData()) {
-            rootPart = rootPartAnnotation.getReplacementData();
-        } else {
-            rootPart = primaryPart;
-        }
-
-        return new CryptoMessageParts(rootPart, rootPartAnnotation, extraParts);
-    }
-
-    public static class CryptoMessageParts {
-        public final Part contentPart;
-        @Nullable
-        public final CryptoResultAnnotation contentCryptoAnnotation;
-
-        public final List<Part> extraParts;
-
-        CryptoMessageParts(Part contentPart, @Nullable CryptoResultAnnotation contentCryptoAnnotation, List<Part> extraParts) {
-            this.contentPart = contentPart;
-            this.contentCryptoAnnotation = contentCryptoAnnotation;
-            this.extraParts = Collections.unmodifiableList(extraParts);
-        }
-    }
-
-}
diff --git a/k9mail/src/test/java/com/fsck/k9/mailstore/MessageViewInfoExtractorTest.java b/k9mail/src/test/java/com/fsck/k9/mailstore/MessageViewInfoExtractorTest.java
index 45588eef4..2d799629d 100644
--- a/k9mail/src/test/java/com/fsck/k9/mailstore/MessageViewInfoExtractorTest.java
+++ b/k9mail/src/test/java/com/fsck/k9/mailstore/MessageViewInfoExtractorTest.java
@@ -9,11 +9,14 @@ import java.util.Locale;
 import java.util.TimeZone;
 
 import android.app.Application;
+import android.support.annotation.NonNull;
 
 import com.fsck.k9.GlobalsHelper;
 import com.fsck.k9.K9RobolectricTestRunner;
 import com.fsck.k9.activity.K9ActivityCommon;
 import com.fsck.k9.mail.Address;
+import com.fsck.k9.mail.BodyPart;
+import com.fsck.k9.mail.Message;
 import com.fsck.k9.mail.Message.RecipientType;
 import com.fsck.k9.mail.MessagingException;
 import com.fsck.k9.mail.Part;
@@ -26,8 +29,11 @@ import com.fsck.k9.mail.internet.MimeMultipart;
 import com.fsck.k9.mail.internet.TextBody;
 import com.fsck.k9.mail.internet.Viewable;
 import com.fsck.k9.mail.internet.Viewable.MessageHeader;
+import com.fsck.k9.mailstore.CryptoResultAnnotation.CryptoError;
 import com.fsck.k9.mailstore.MessageViewInfoExtractor.ViewableExtractedText;
+import com.fsck.k9.message.extractors.AttachmentInfoExtractor;
 import com.fsck.k9.message.html.HtmlProcessor;
+import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -35,13 +41,21 @@ import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 import org.robolectric.RuntimeEnvironment;
 
+import static com.fsck.k9.message.TestMessageConstructionUtils.bodypart;
+import static com.fsck.k9.message.TestMessageConstructionUtils.messageFromBody;
+import static com.fsck.k9.message.TestMessageConstructionUtils.multipart;
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertSame;
+import static junit.framework.Assert.assertTrue;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 
+@SuppressWarnings("WeakerAccess")
 @RunWith(K9RobolectricTestRunner.class)
 public class MessageViewInfoExtractorTest {
     public static final String BODY_TEXT = "K-9 Mail rocks :>";
@@ -51,6 +65,7 @@ public class MessageViewInfoExtractorTest {
 
     private MessageViewInfoExtractor messageViewInfoExtractor;
     private Application context;
+    private AttachmentInfoExtractor attachmentInfoExtractor;
 
 
     @Before
@@ -60,7 +75,8 @@ public class MessageViewInfoExtractorTest {
         GlobalsHelper.setContext(context);
 
         HtmlProcessor htmlProcessor = createFakeHtmlProcessor();
-        messageViewInfoExtractor = new MessageViewInfoExtractor(context,null, htmlProcessor);
+        attachmentInfoExtractor = spy(AttachmentInfoExtractor.getInstance());
+        messageViewInfoExtractor = new MessageViewInfoExtractor(context, attachmentInfoExtractor, htmlProcessor);
     }
 
     @Test
@@ -359,6 +375,190 @@ public class MessageViewInfoExtractorTest {
         assertEquals(expectedHtmlText, firstMessageExtractedText.html);
     }
 
+    @Test
+    public void extractMessage_withAttachment() throws Exception {
+        BodyPart attachmentPart = bodypart("application/octet-stream");
+        Message message = messageFromBody(multipart("mixed",
+                bodypart("text/plain", "text"),
+                attachmentPart
+        ));
+        AttachmentViewInfo attachmentViewInfo = mock(AttachmentViewInfo.class);
+        setupAttachmentInfoForPart(attachmentPart, attachmentViewInfo);
+
+
+        MessageViewInfo messageViewInfo = messageViewInfoExtractor.extractMessageForView(message, null);
+
+
+        assertEquals("<pre class=\\"k9mail\\">text</pre>", messageViewInfo.text);
+        assertSame(attachmentViewInfo, messageViewInfo.attachments.get(0));
+        assertNull(messageViewInfo.cryptoResultAnnotation);
+        assertTrue(messageViewInfo.extraAttachments.isEmpty());
+    }
+
+    @Test
+    public void extractMessage_withCryptoAnnotation() throws Exception {
+        Message message = messageFromBody(multipart("signed", "protocol=\\"application/pgp-signature\\"",
+                bodypart("text/plain", "text"),
+                bodypart("application/pgp-signature")
+        ));
+        CryptoResultAnnotation annotation = CryptoResultAnnotation.createOpenPgpResultAnnotation(
+                null, null, null, null, null, false);
+        MessageCryptoAnnotations messageCryptoAnnotations = createAnnotations(message, annotation);
+
+
+        MessageViewInfo messageViewInfo = messageViewInfoExtractor.extractMessageForView(message, messageCryptoAnnotations);
+
+
+        assertEquals("<pre class=\\"k9mail\\">text</pre>", messageViewInfo.text);
+        assertSame(annotation, messageViewInfo.cryptoResultAnnotation);
+        assertTrue(messageViewInfo.attachments.isEmpty());
+        assertTrue(messageViewInfo.extraAttachments.isEmpty());
+    }
+
+    @Test
+    public void extractMessage_withCryptoAnnotation_andReplacementPart() throws Exception {
+        Message message = messageFromBody(multipart("signed", "protocol=\\"application/pgp-signature\\"",
+                bodypart("text/plain", "text"),
+                bodypart("application/pgp-signature")
+        ));
+        MimeBodyPart replacementPart = bodypart("text/plain", "replacement text");
+        CryptoResultAnnotation annotation = CryptoResultAnnotation.createOpenPgpResultAnnotation(
+                null, null, null, null, replacementPart, false);
+        MessageCryptoAnnotations messageCryptoAnnotations = createAnnotations(message, annotation);
+
+
+        MessageViewInfo messageViewInfo = messageViewInfoExtractor.extractMessageForView(message, messageCryptoAnnotations);
+
+
+        assertEquals("<pre class=\\"k9mail\\">replacement text</pre>", messageViewInfo.text);
+        assertSame(annotation, messageViewInfo.cryptoResultAnnotation);
+        assertTrue(messageViewInfo.attachments.isEmpty());
+        assertTrue(messageViewInfo.extraAttachments.isEmpty());
+    }
+
+    @Test
+    public void extractMessage_withCryptoAnnotation_andExtraText() throws Exception {
+        MimeBodyPart signedPart = multipart("signed", "protocol=\\"application/pgp-signature\\"",
+                bodypart("text/plain", "text"),
+                bodypart("application/pgp-signature")
+        );
+        BodyPart extraText = bodypart("text/plain", "extra text");
+        Message message = messageFromBody(multipart("mixed",
+                signedPart,
+                extraText
+        ));
+        CryptoResultAnnotation annotation = CryptoResultAnnotation.createOpenPgpResultAnnotation(
+                null, null, null, null, null, false);
+        MessageCryptoAnnotations messageCryptoAnnotations = createAnnotations(signedPart, annotation);
+
+
+        MessageViewInfo messageViewInfo = messageViewInfoExtractor.extractMessageForView(message, messageCryptoAnnotations);
+
+
+        assertEquals("<pre class=\\"k9mail\\">text</pre>", messageViewInfo.text);
+        assertSame(annotation, messageViewInfo.cryptoResultAnnotation);
+        assertEquals("extra text", messageViewInfo.extraText);
+        assertTrue(messageViewInfo.attachments.isEmpty());
+        assertTrue(messageViewInfo.extraAttachments.isEmpty());
+    }
+
+    @Test
+    public void extractMessage_withCryptoAnnotation_andExtraAttachment() throws Exception {
+        MimeBodyPart signedPart = multipart("signed", "protocol=\\"application/pgp-signature\\"",
+                bodypart("text/plain", "text"),
+                bodypart("application/pgp-signature")
+        );
+        BodyPart extraAttachment = bodypart("application/octet-stream");
+        Message message = messageFromBody(multipart("mixed",
+                signedPart,
+                extraAttachment
+        ));
+        CryptoResultAnnotation annotation = CryptoResultAnnotation.createOpenPgpResultAnnotation(
+                null, null, null, null, null, false);
+        MessageCryptoAnnotations messageCryptoAnnotations = createAnnotations(signedPart, annotation);
+
+        AttachmentViewInfo attachmentViewInfo = mock(AttachmentViewInfo.class);
+        setupAttachmentInfoForPart(extraAttachment, attachmentViewInfo);
+
+
+        MessageViewInfo messageViewInfo = messageViewInfoExtractor.extractMessageForView(message, messageCryptoAnnotations);
+
+
+        assertEquals("<pre class=\\"k9mail\\">text</pre>", messageViewInfo.text);
+        assertSame(annotation, messageViewInfo.cryptoResultAnnotation);
+        assertSame(attachmentViewInfo, messageViewInfo.extraAttachments.get(0));
+        assertTrue(messageViewInfo.attachments.isEmpty());
+    }
+
+    @Test
+    public void extractMessage_openPgpEncrypted_withoutAnnotations() throws Exception {
+        Message message = messageFromBody(
+                multipart("encrypted", "protocol=\\"application/pgp-encrypted\\"",
+                        bodypart("application/pgp-encrypted"),
+                        bodypart("application/octet-stream")
+                )
+        );
+
+        MessageViewInfo messageViewInfo = messageViewInfoExtractor.extractMessageForView(message, null);
+
+        assertEquals(CryptoError.OPENPGP_ENCRYPTED_NO_PROVIDER, messageViewInfo.cryptoResultAnnotation.getErrorType());
+        assertNull(messageViewInfo.text);
+        assertNull(messageViewInfo.attachments);
+        assertNull(messageViewInfo.extraAttachments);
+    }
+
+    @Test
+    public void extractMessage_multipartSigned_UnknownProtocol() throws Exception {
+        Message message = messageFromBody(
+                multipart("signed", "protocol=\\"application/pkcs7-signature\\"",
+                    bodypart("text/plain", "text"),
+                    bodypart("application/pkcs7-signature", "signature")
+                )
+        );
+
+        MessageViewInfo messageViewInfo = messageViewInfoExtractor.extractMessageForView(message, null);
+
+        assertEquals("<pre class=\\"k9mail\\">text</pre>", messageViewInfo.text);
+        assertNull(messageViewInfo.cryptoResultAnnotation);
+        assertTrue(messageViewInfo.attachments.isEmpty());
+        assertTrue(messageViewInfo.extraAttachments.isEmpty());
+    }
+
+    @Test
+    public void extractMessage_multipartSigned_UnknownProtocol_withExtraAttachments() throws Exception {
+        BodyPart extraAttachment = bodypart("application/octet-stream");
+        Message message = messageFromBody(
+                multipart("mixed",
+                        multipart("signed", "protocol=\\"application/pkcs7-signature\\"",
+                                bodypart("text/plain", "text"),
+                                bodypart("application/pkcs7-signature", "signature")
+                        ),
+                        extraAttachment
+                )
+        );
+        AttachmentViewInfo mock = mock(AttachmentViewInfo.class);
+        setupAttachmentInfoForPart(extraAttachment, mock);
+
+        MessageViewInfo messageViewInfo = messageViewInfoExtractor.extractMessageForView(message, null);
+
+        assertEquals("<pre class=\\"k9mail\\">text</pre>", messageViewInfo.text);
+        assertNull(messageViewInfo.cryptoResultAnnotation);
+        assertSame(mock, messageViewInfo.attachments.get(0));
+        assertTrue(messageViewInfo.extraAttachments.isEmpty());
+    }
+
+    void setupAttachmentInfoForPart(BodyPart extraAttachment, AttachmentViewInfo attachmentViewInfo)
+            throws MessagingException {
+        doReturn(attachmentViewInfo).when(attachmentInfoExtractor).extractAttachmentInfo(extraAttachment);
+    }
+
+    @NonNull
+    MessageCryptoAnnotations createAnnotations(Part part, CryptoResultAnnotation annotation) {
+        MessageCryptoAnnotations messageCryptoAnnotations = new MessageCryptoAnnotations();
+        messageCryptoAnnotations.put(part, annotation);
+        return messageCryptoAnnotations;
+    }
+
     HtmlProcessor createFakeHtmlProcessor() {
         HtmlProcessor htmlProcessor = mock(HtmlProcessor.class);
 
diff --git a/k9mail/src/test/java/com/fsck/k9/message/TestMessageConstructionUtils.java b/k9mail/src/test/java/com/fsck/k9/message/TestMessageConstructionUtils.java
index 45ee4b908..07e813eaa 100644
--- a/k9mail/src/test/java/com/fsck/k9/message/TestMessageConstructionUtils.java
+++ b/k9mail/src/test/java/com/fsck/k9/message/TestMessageConstructionUtils.java
@@ -45,7 +45,7 @@ public class TestMessageConstructionUtils {
         return new MimeBodyPart(null, type);
     }
 
-    public static BodyPart bodypart(String type, String text) throws MessagingException {
+    public static MimeBodyPart bodypart(String type, String text) throws MessagingException {
         TextBody textBody = new TextBody(text);
         return new MimeBodyPart(textBody, type);
     }