From 6f3f555986e46682ac87c87e86c5e20d27229843 Mon Sep 17 00:00:00 2001 From: cketti Date: Sat, 21 Feb 2015 00:29:27 +0100 Subject: [PATCH] Add support for new decrypt/verify error conditions We can decrypt or verify a message if it was only partly downloaded. --- .../k9/mailstore/LocalMessageExtractor.java | 17 +----- .../com/fsck/k9/mailstore/MessageHelper.java | 37 ++++++++++++ .../fsck/k9/mailstore/MessageViewInfo.java | 20 +------ .../k9/mailstore/OpenPgpResultAnnotation.java | 19 +++++- .../k9/ui/crypto/MessageCryptoHelper.java | 27 +++++---- .../ui/messageview/MessageContainerView.java | 59 +++++++++++++------ 6 files changed, 118 insertions(+), 61 deletions(-) create mode 100644 k9mail/src/main/java/com/fsck/k9/mailstore/MessageHelper.java diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java index d15caf7b0..2835046a4 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java @@ -1,6 +1,5 @@ package com.fsck.k9.mailstore; -import android.app.PendingIntent; import android.content.Context; import android.net.Uri; @@ -22,8 +21,6 @@ import com.fsck.k9.mailstore.MessageViewInfo.MessageViewContainer; import com.fsck.k9.provider.AttachmentProvider; import com.fsck.k9.provider.K9FileProvider; import com.fsck.k9.ui.crypto.MessageCryptoAnnotations; -import org.openintents.openpgp.OpenPgpError; -import org.openintents.openpgp.OpenPgpSignatureResult; import java.io.File; import java.util.ArrayList; @@ -448,18 +445,8 @@ public class LocalMessageExtractor { attachments); List attachmentInfos = extractAttachmentInfos(context, attachments); - MessageViewContainer messageViewContainer; - if (pgpAnnotation == NO_ANNOTATIONS) { - messageViewContainer = new MessageViewContainer(viewable.html, part, attachmentInfos); - } else { - OpenPgpSignatureResult pgpResult = pgpAnnotation.getSignatureResult(); - OpenPgpError pgpError = pgpAnnotation.getError(); - boolean wasEncrypted = pgpAnnotation.wasEncrypted(); - PendingIntent pendingIntent = pgpAnnotation.getPendingIntent(); - - messageViewContainer = new MessageViewContainer(viewable.html, part, attachmentInfos, pgpResult, - pgpError, wasEncrypted, pendingIntent); - } + MessageViewContainer messageViewContainer = + new MessageViewContainer(viewable.html, part, attachmentInfos, pgpAnnotation); containers.add(messageViewContainer); } diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/MessageHelper.java b/k9mail/src/main/java/com/fsck/k9/mailstore/MessageHelper.java new file mode 100644 index 000000000..e7747aa8d --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/MessageHelper.java @@ -0,0 +1,37 @@ +package com.fsck.k9.mailstore; + + +import java.util.Stack; + +import com.fsck.k9.mail.Body; +import com.fsck.k9.mail.BodyPart; +import com.fsck.k9.mail.Multipart; +import com.fsck.k9.mail.Part; + + +public class MessageHelper { + + public static boolean isCompletePartAvailable(Part part) { + Stack partsToCheck = new Stack(); + partsToCheck.push(part); + + while (!partsToCheck.isEmpty()) { + Part currentPart = partsToCheck.pop(); + Body body = currentPart.getBody(); + + boolean isBodyMissing = body == null; + if (isBodyMissing) { + return false; + } + + if (body instanceof Multipart) { + Multipart multipart = (Multipart) body; + for (BodyPart bodyPart : multipart.getBodyParts()) { + partsToCheck.push(bodyPart); + } + } + } + + return true; + } +} 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 857f42e2c..888f59ea3 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/MessageViewInfo.java @@ -3,12 +3,8 @@ package com.fsck.k9.mailstore; import java.util.List; -import android.app.PendingIntent; - import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Part; -import org.openintents.openpgp.OpenPgpError; -import org.openintents.openpgp.OpenPgpSignatureResult; public class MessageViewInfo { @@ -26,25 +22,15 @@ public class MessageViewInfo { public final String text; public final Part rootPart; public final List attachments; - public final boolean encrypted; - public final OpenPgpSignatureResult signatureResult; - public final OpenPgpError pgpError; - public final PendingIntent pgpPendingIntent; + public final OpenPgpResultAnnotation cryptoAnnotation; - MessageViewContainer(String text, Part rootPart, List attachments) { - this(text, rootPart, attachments, null, null, false, null); - } MessageViewContainer(String text, Part rootPart, List attachments, - OpenPgpSignatureResult signatureResult, OpenPgpError pgpError, boolean encrypted, - PendingIntent pgpPendingIntent) { + OpenPgpResultAnnotation cryptoAnnotation) { this.text = text; this.rootPart = rootPart; this.attachments = attachments; - this.signatureResult = signatureResult; - this.pgpError = pgpError; - this.encrypted = encrypted; - this.pgpPendingIntent = pgpPendingIntent; + this.cryptoAnnotation = cryptoAnnotation; } } } diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/OpenPgpResultAnnotation.java b/k9mail/src/main/java/com/fsck/k9/mailstore/OpenPgpResultAnnotation.java index ad77177a3..797709afa 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/OpenPgpResultAnnotation.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/OpenPgpResultAnnotation.java @@ -8,10 +8,11 @@ import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpSignatureResult; -public class OpenPgpResultAnnotation { +public final class OpenPgpResultAnnotation { private boolean wasEncrypted; private OpenPgpSignatureResult signatureResult; private OpenPgpError error; + private CryptoError errorType = CryptoError.NONE; private PendingIntent pendingIntent; private MimeBodyPart outputData; @@ -37,6 +38,15 @@ public class OpenPgpResultAnnotation { public void setError(OpenPgpError error) { this.error = error; + setErrorType(CryptoError.CRYPTO_API_RETURNED_ERROR); + } + + public CryptoError getErrorType() { + return errorType; + } + + public void setErrorType(CryptoError errorType) { + this.errorType = errorType; } public boolean hasOutputData() { @@ -59,4 +69,11 @@ public class OpenPgpResultAnnotation { this.wasEncrypted = wasEncrypted; } + + public static enum CryptoError { + NONE, + CRYPTO_API_RETURNED_ERROR, + SIGNED_BUT_INCOMPLETE, + ENCRYPTED_BUT_INCOMPLETE + } } diff --git a/k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoHelper.java b/k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoHelper.java index 3cc0147fb..fd68f0d87 100644 --- a/k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoHelper.java +++ b/k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoHelper.java @@ -34,7 +34,9 @@ import com.fsck.k9.mail.internet.MimeBodyPart; import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mailstore.DecryptStreamParser; import com.fsck.k9.mailstore.LocalMessage; +import com.fsck.k9.mailstore.MessageHelper; import com.fsck.k9.mailstore.OpenPgpResultAnnotation; +import com.fsck.k9.mailstore.OpenPgpResultAnnotation.CryptoError; import org.openintents.openpgp.IOpenPgpService; import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpSignatureResult; @@ -101,21 +103,24 @@ public class MessageCryptoHelper { } Part part = partsToDecryptOrVerify.peekFirst(); - if ("text/plain".equalsIgnoreCase(part.getMimeType())) { - startDecryptingOrVerifyingPart(part); - } else if (MessageDecryptVerifier.isPgpMimePart(part)) { - Multipart multipart = (Multipart) part.getBody(); - if (multipart == null) { - throw new RuntimeException("Downloading missing parts before decryption isn't supported yet"); - } - - startDecryptingOrVerifyingPart(part); + if (!MessageHelper.isCompletePartAvailable(part)) { + addErrorAnnotation(part); } else { - partsToDecryptOrVerify.removeFirst(); - decryptOrVerifyNextPart(); + startDecryptingOrVerifyingPart(part); } } + private void addErrorAnnotation(Part part) { + OpenPgpResultAnnotation annotation = new OpenPgpResultAnnotation(); + if (MessageDecryptVerifier.isPgpMimeSignedPart(part)) { + annotation.setErrorType(CryptoError.SIGNED_BUT_INCOMPLETE); + } else { + annotation.setErrorType(CryptoError.ENCRYPTED_BUT_INCOMPLETE); + } + messageAnnotations.put(part, annotation); + onCryptoFinished(); + } + private void startDecryptingOrVerifyingPart(Part part) { if (!isBoundToCryptoProviderService()) { connectToCryptoProviderService(); diff --git a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageContainerView.java b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageContainerView.java index c233b06a3..2dd32d1d8 100644 --- a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageContainerView.java +++ b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageContainerView.java @@ -37,10 +37,11 @@ import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mailstore.AttachmentViewInfo; import com.fsck.k9.mailstore.MessageViewInfo.MessageViewContainer; +import com.fsck.k9.mailstore.OpenPgpResultAnnotation; +import com.fsck.k9.mailstore.OpenPgpResultAnnotation.CryptoError; import com.fsck.k9.view.K9WebViewClient; import com.fsck.k9.view.MessageHeader.OnLayoutChangedListener; import com.fsck.k9.view.MessageWebView; -import org.openintents.openpgp.OpenPgpError; public class MessageContainerView extends LinearLayout implements OnClickListener, @@ -374,15 +375,6 @@ public class MessageContainerView extends LinearLayout implements OnClickListene WebViewClient webViewClient = K9WebViewClient.newInstance(messageViewContainer.rootPart); mMessageContentView.setWebViewClient(webViewClient); - // Save the text so we can reset the WebView when the user clicks the "Show pictures" button - OpenPgpError error = messageViewContainer.pgpError; - if (error != null) { - // TODO make a nice view for this - mText = error.getMessage(); - } else { - mText = messageViewContainer.text; - } - boolean hasAttachments = !messageViewContainer.attachments.isEmpty(); if (hasAttachments) { renderAttachments(messageViewContainer); @@ -404,6 +396,7 @@ public class MessageContainerView extends LinearLayout implements OnClickListene mSavedState = null; } + mText = getTextToDisplay(messageViewContainer); if (mText != null && lookForImages) { if (Utility.hasExternalImages(mText) && !isShowingPictures()) { if (automaticallyLoadPictures) { @@ -418,24 +411,56 @@ public class MessageContainerView extends LinearLayout implements OnClickListene ViewStub openPgpHeaderStub = (ViewStub) findViewById(R.id.openpgp_header_stub); OpenPgpHeaderView openPgpHeaderView = (OpenPgpHeaderView) openPgpHeaderStub.inflate(); - openPgpHeaderView.setOpenPgpData(messageViewContainer.signatureResult, messageViewContainer.encrypted, - messageViewContainer.pgpPendingIntent); + OpenPgpResultAnnotation cryptoAnnotation = messageViewContainer.cryptoAnnotation; + if (cryptoAnnotation == null) { + openPgpHeaderView.setOpenPgpData(null, false, null); + } else { + openPgpHeaderView.setOpenPgpData(cryptoAnnotation.getSignatureResult(), cryptoAnnotation.wasEncrypted(), + cryptoAnnotation.getPendingIntent()); + } openPgpHeaderView.setCallback(openPgpHeaderViewCallback); mSidebar.setVisibility(View.VISIBLE); } else { mSidebar.setVisibility(View.GONE); } + String text; if (mText != null) { - loadBodyFromText(mText); + text = mText; } else { - showStatusMessage(getContext().getString(R.string.webview_empty_message)); + text = wrapStatusMessage(getContext().getString(R.string.webview_empty_message)); } + + loadBodyFromText(text); } - public void showStatusMessage(String status) { - String text = "
" + status + "
"; - loadBodyFromText(text); + private String getTextToDisplay(MessageViewContainer messageViewContainer) { + OpenPgpResultAnnotation cryptoAnnotation = messageViewContainer.cryptoAnnotation; + if (cryptoAnnotation == null) { + return messageViewContainer.text; + } + + CryptoError errorType = cryptoAnnotation.getErrorType(); + switch (errorType) { + case CRYPTO_API_RETURNED_ERROR: { + // TODO make a nice view for this + return wrapStatusMessage(cryptoAnnotation.getError().getMessage()); + } + case ENCRYPTED_BUT_INCOMPLETE: { + //FIXME + return wrapStatusMessage("You need to download the complete message to be able to decrypt it."); + } + case NONE: + case SIGNED_BUT_INCOMPLETE: { + return messageViewContainer.text; + } + } + + throw new IllegalStateException("Unknown error type: " + errorType); + } + + public String wrapStatusMessage(String status) { + return "
" + status + "
"; } private void loadBodyFromText(String emailText) {