Merge pull request #2763 from k9mail/improve-crypto-structure-detection
Improve crypto structure detection
This commit is contained in:
commit
e266547bfc
14 changed files with 469 additions and 269 deletions
|
@ -162,6 +162,11 @@ public class MessageExtractor {
|
||||||
Alternative alternative = new Alternative(text, html);
|
Alternative alternative = new Alternative(text, html);
|
||||||
outputViewableParts.add(alternative);
|
outputViewableParts.add(alternative);
|
||||||
}
|
}
|
||||||
|
} else if (isSameMimeType(part.getMimeType(), "multipart/signed")) {
|
||||||
|
if (multipart.getCount() > 0) {
|
||||||
|
BodyPart bodyPart = multipart.getBodyPart(0);
|
||||||
|
findViewablesAndAttachments(bodyPart, outputViewableParts, outputNonViewableParts);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// For all other multipart parts we recurse to grab all viewable children.
|
// For all other multipart parts we recurse to grab all viewable children.
|
||||||
for (Part bodyPart : multipart.getBodyParts()) {
|
for (Part bodyPart : multipart.getBodyParts()) {
|
||||||
|
|
|
@ -30,10 +30,10 @@ public class MimeBodyPart extends BodyPart {
|
||||||
this(body, null);
|
this(body, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MimeBodyPart(Body body, String mimeType) throws MessagingException {
|
public MimeBodyPart(Body body, String contentType) throws MessagingException {
|
||||||
mHeader = new MimeHeader();
|
mHeader = new MimeHeader();
|
||||||
if (mimeType != null) {
|
if (contentType != null) {
|
||||||
addHeader(MimeHeader.HEADER_CONTENT_TYPE, mimeType);
|
addHeader(MimeHeader.HEADER_CONTENT_TYPE, contentType);
|
||||||
}
|
}
|
||||||
MimeMessageHelper.setBody(this, body);
|
MimeMessageHelper.setBody(this, body);
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,7 @@ public class MessageLoaderHelper {
|
||||||
private LoaderManager loaderManager;
|
private LoaderManager loaderManager;
|
||||||
@Nullable // make this explicitly nullable, make sure to cancel/ignore any operation if this is null
|
@Nullable // make this explicitly nullable, make sure to cancel/ignore any operation if this is null
|
||||||
private MessageLoaderCallbacks callback;
|
private MessageLoaderCallbacks callback;
|
||||||
|
private final boolean processSignedOnly;
|
||||||
|
|
||||||
|
|
||||||
// transient state
|
// transient state
|
||||||
|
@ -100,6 +101,8 @@ public class MessageLoaderHelper {
|
||||||
this.loaderManager = loaderManager;
|
this.loaderManager = loaderManager;
|
||||||
this.fragmentManager = fragmentManager;
|
this.fragmentManager = fragmentManager;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
|
||||||
|
processSignedOnly = K9.getOpenPgpSupportSignOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -276,7 +279,7 @@ public class MessageLoaderHelper {
|
||||||
retainCryptoHelperFragment.setData(messageCryptoHelper);
|
retainCryptoHelperFragment.setData(messageCryptoHelper);
|
||||||
}
|
}
|
||||||
messageCryptoHelper.asyncStartOrResumeProcessingMessage(
|
messageCryptoHelper.asyncStartOrResumeProcessingMessage(
|
||||||
localMessage, messageCryptoCallback, cachedDecryptionResult);
|
localMessage, messageCryptoCallback, cachedDecryptionResult, processSignedOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cancelAndClearCryptoOperation() {
|
private void cancelAndClearCryptoOperation() {
|
||||||
|
|
|
@ -18,6 +18,7 @@ import com.fsck.k9.mail.Multipart;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
import com.fsck.k9.mail.internet.MessageExtractor;
|
import com.fsck.k9.mail.internet.MessageExtractor;
|
||||||
import com.fsck.k9.mail.internet.MimeBodyPart;
|
import com.fsck.k9.mail.internet.MimeBodyPart;
|
||||||
|
import com.fsck.k9.mail.internet.MimeMultipart;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mailstore.CryptoResultAnnotation;
|
import com.fsck.k9.mailstore.CryptoResultAnnotation;
|
||||||
import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
|
import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
|
||||||
|
@ -25,7 +26,7 @@ import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
|
||||||
import static com.fsck.k9.mail.internet.MimeUtility.isSameMimeType;
|
import static com.fsck.k9.mail.internet.MimeUtility.isSameMimeType;
|
||||||
|
|
||||||
|
|
||||||
public class MessageDecryptVerifier {
|
public class MessageCryptoStructureDetector {
|
||||||
private static final String MULTIPART_ENCRYPTED = "multipart/encrypted";
|
private static final String MULTIPART_ENCRYPTED = "multipart/encrypted";
|
||||||
private static final String MULTIPART_SIGNED = "multipart/signed";
|
private static final String MULTIPART_SIGNED = "multipart/signed";
|
||||||
private static final String PROTOCOL_PARAMETER = "protocol";
|
private static final String PROTOCOL_PARAMETER = "protocol";
|
||||||
|
@ -109,7 +110,7 @@ public class MessageDecryptVerifier {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Part> findEncryptedParts(Part startPart) {
|
public static List<Part> findMultipartEncryptedParts(Part startPart) {
|
||||||
List<Part> encryptedParts = new ArrayList<>();
|
List<Part> encryptedParts = new ArrayList<>();
|
||||||
Stack<Part> partsToCheck = new Stack<>();
|
Stack<Part> partsToCheck = new Stack<>();
|
||||||
partsToCheck.push(startPart);
|
partsToCheck.push(startPart);
|
||||||
|
@ -135,7 +136,7 @@ public class MessageDecryptVerifier {
|
||||||
return encryptedParts;
|
return encryptedParts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Part> findSignedParts(Part startPart, MessageCryptoAnnotations messageCryptoAnnotations) {
|
public static List<Part> findMultipartSignedParts(Part startPart, MessageCryptoAnnotations messageCryptoAnnotations) {
|
||||||
List<Part> signedParts = new ArrayList<>();
|
List<Part> signedParts = new ArrayList<>();
|
||||||
Stack<Part> partsToCheck = new Stack<>();
|
Stack<Part> partsToCheck = new Stack<>();
|
||||||
partsToCheck.push(startPart);
|
partsToCheck.push(startPart);
|
||||||
|
@ -214,24 +215,59 @@ public class MessageDecryptVerifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isPartMultipartSigned(Part part) {
|
private static boolean isPartMultipartSigned(Part part) {
|
||||||
return isSameMimeType(part.getMimeType(), MULTIPART_SIGNED);
|
if (!isSameMimeType(part.getMimeType(), MULTIPART_SIGNED)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (! (part.getBody() instanceof MimeMultipart)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MimeMultipart mimeMultipart = (MimeMultipart) part.getBody();
|
||||||
|
if (mimeMultipart.getCount() != 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String protocolParameter = MimeUtility.getHeaderParameter(part.getContentType(), PROTOCOL_PARAMETER);
|
||||||
|
|
||||||
|
boolean dataUnavailable = protocolParameter == null && mimeMultipart.getBodyPart(0).getBody() == null;
|
||||||
|
boolean protocolMatches = isSameMimeType(protocolParameter, mimeMultipart.getBodyPart(1).getMimeType());
|
||||||
|
return dataUnavailable || protocolMatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isPartMultipartEncrypted(Part part) {
|
private static boolean isPartMultipartEncrypted(Part part) {
|
||||||
return isSameMimeType(part.getMimeType(), MULTIPART_ENCRYPTED);
|
if (!isSameMimeType(part.getMimeType(), MULTIPART_ENCRYPTED)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (! (part.getBody() instanceof MimeMultipart)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MimeMultipart mimeMultipart = (MimeMultipart) part.getBody();
|
||||||
|
if (mimeMultipart.getCount() != 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String protocolParameter = MimeUtility.getHeaderParameter(part.getContentType(), PROTOCOL_PARAMETER);
|
||||||
|
|
||||||
|
boolean dataUnavailable = protocolParameter == null && mimeMultipart.getBodyPart(1).getBody() == null;
|
||||||
|
boolean protocolMatches = isSameMimeType(protocolParameter, mimeMultipart.getBodyPart(0).getMimeType());
|
||||||
|
return dataUnavailable || protocolMatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO also guess by mime-type of contained part?
|
public static boolean isMultipartEncryptedOpenPgpProtocol(Part part) {
|
||||||
public static boolean isPgpMimeEncryptedOrSignedPart(Part part) {
|
if (!isSameMimeType(part.getMimeType(), MULTIPART_ENCRYPTED)) {
|
||||||
String contentType = part.getContentType();
|
throw new IllegalArgumentException("Part is not multipart/encrypted!");
|
||||||
String protocolParameter = MimeUtility.getHeaderParameter(contentType, PROTOCOL_PARAMETER);
|
}
|
||||||
|
|
||||||
boolean isPgpEncrypted = isSameMimeType(part.getMimeType(), MULTIPART_ENCRYPTED) &&
|
String protocolParameter = MimeUtility.getHeaderParameter(part.getContentType(), PROTOCOL_PARAMETER);
|
||||||
APPLICATION_PGP_ENCRYPTED.equalsIgnoreCase(protocolParameter);
|
return APPLICATION_PGP_ENCRYPTED.equalsIgnoreCase(protocolParameter);
|
||||||
boolean isPgpSigned = isSameMimeType(part.getMimeType(), MULTIPART_SIGNED) &&
|
}
|
||||||
APPLICATION_PGP_SIGNATURE.equalsIgnoreCase(protocolParameter);
|
|
||||||
|
|
||||||
return isPgpEncrypted || isPgpSigned;
|
public static boolean isMultipartSignedOpenPgpProtocol(Part part) {
|
||||||
|
if (!isSameMimeType(part.getMimeType(), MULTIPART_SIGNED)) {
|
||||||
|
throw new IllegalArgumentException("Part is not multipart/signed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
String protocolParameter = MimeUtility.getHeaderParameter(part.getContentType(), PROTOCOL_PARAMETER);
|
||||||
|
return APPLICATION_PGP_SIGNATURE.equalsIgnoreCase(protocolParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
|
@ -28,6 +28,7 @@ import com.fsck.k9.message.html.HtmlProcessor;
|
||||||
import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
|
import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
|
||||||
import com.fsck.k9.ui.crypto.MessageCryptoSplitter;
|
import com.fsck.k9.ui.crypto.MessageCryptoSplitter;
|
||||||
import com.fsck.k9.ui.crypto.MessageCryptoSplitter.CryptoMessageParts;
|
import com.fsck.k9.ui.crypto.MessageCryptoSplitter.CryptoMessageParts;
|
||||||
|
import org.openintents.openpgp.util.OpenPgpUtils;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static com.fsck.k9.mail.internet.MimeUtility.getHeaderParameter;
|
import static com.fsck.k9.mail.internet.MimeUtility.getHeaderParameter;
|
||||||
|
@ -227,7 +228,7 @@ public class MessageViewInfoExtractor {
|
||||||
Part part = ((Textual)viewable).getPart();
|
Part part = ((Textual)viewable).getPart();
|
||||||
addHtmlDivider(html, part, prependDivider);
|
addHtmlDivider(html, part, prependDivider);
|
||||||
|
|
||||||
String t = MessageExtractor.getTextFromPart(part);
|
String t = getTextFromPart(part);
|
||||||
if (t == null) {
|
if (t == null) {
|
||||||
t = "";
|
t = "";
|
||||||
} else if (viewable instanceof Flowed) {
|
} else if (viewable instanceof Flowed) {
|
||||||
|
@ -264,7 +265,7 @@ public class MessageViewInfoExtractor {
|
||||||
Part part = ((Textual)viewable).getPart();
|
Part part = ((Textual)viewable).getPart();
|
||||||
addTextDivider(text, part, prependDivider);
|
addTextDivider(text, part, prependDivider);
|
||||||
|
|
||||||
String t = MessageExtractor.getTextFromPart(part);
|
String t = getTextFromPart(part);
|
||||||
if (t == null) {
|
if (t == null) {
|
||||||
t = "";
|
t = "";
|
||||||
} else if (viewable instanceof Html) {
|
} else if (viewable instanceof Html) {
|
||||||
|
@ -315,6 +316,17 @@ public class MessageViewInfoExtractor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getTextFromPart(Part part) {
|
||||||
|
String textFromPart = MessageExtractor.getTextFromPart(part);
|
||||||
|
|
||||||
|
String extractedClearsignedMessage = OpenPgpUtils.extractClearsignedMessage(textFromPart);
|
||||||
|
if (extractedClearsignedMessage != null) {
|
||||||
|
textFromPart = extractedClearsignedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return textFromPart;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of the message part.
|
* Get the name of the message part.
|
||||||
*
|
*
|
||||||
|
@ -504,7 +516,7 @@ public class MessageViewInfoExtractor {
|
||||||
public final String text;
|
public final String text;
|
||||||
public final String html;
|
public final String html;
|
||||||
|
|
||||||
public ViewableExtractedText(String text, String html) {
|
ViewableExtractedText(String text, String html) {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.html = html;
|
this.html = html;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.fsck.k9.message;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.fsck.k9.crypto.MessageDecryptVerifier;
|
import com.fsck.k9.crypto.MessageCryptoStructureDetector;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ public class ComposePgpEnableByDefaultDecider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean messageIsEncrypted(Message localMessage) {
|
private boolean messageIsEncrypted(Message localMessage) {
|
||||||
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(localMessage);
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(localMessage);
|
||||||
return !encryptedParts.isEmpty();
|
return !encryptedParts.isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.fsck.k9.message;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.fsck.k9.crypto.MessageDecryptVerifier;
|
import com.fsck.k9.crypto.MessageCryptoStructureDetector;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ public class ComposePgpInlineDecider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean messageHasPgpInlineParts(Message localMessage) {
|
private boolean messageHasPgpInlineParts(Message localMessage) {
|
||||||
List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(localMessage);
|
List<Part> inlineParts = MessageCryptoStructureDetector.findPgpInlineParts(localMessage);
|
||||||
return !inlineParts.isEmpty();
|
return !inlineParts.isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.fsck.k9.message.extractors;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import com.fsck.k9.crypto.MessageDecryptVerifier;
|
import com.fsck.k9.crypto.MessageCryptoStructureDetector;
|
||||||
import com.fsck.k9.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
import com.fsck.k9.mail.BodyPart;
|
import com.fsck.k9.mail.BodyPart;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
|
@ -31,7 +31,7 @@ class EncryptionDetector {
|
||||||
|
|
||||||
private boolean containsInlinePgpEncryptedText(Message message) {
|
private boolean containsInlinePgpEncryptedText(Message message) {
|
||||||
Part textPart = textPartFinder.findFirstTextPart(message);
|
Part textPart = textPartFinder.findFirstTextPart(message);
|
||||||
return MessageDecryptVerifier.isPartPgpInlineEncrypted(textPart);
|
return MessageCryptoStructureDetector.isPartPgpInlineEncrypted(textPart);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean containsPartWithMimeType(Part part, String... wantedMimeTypes) {
|
private boolean containsPartWithMimeType(Part part, String... wantedMimeTypes) {
|
||||||
|
|
|
@ -19,7 +19,7 @@ import android.support.annotation.WorkerThread;
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.autocrypt.AutocryptOperations;
|
import com.fsck.k9.autocrypt.AutocryptOperations;
|
||||||
import com.fsck.k9.crypto.MessageDecryptVerifier;
|
import com.fsck.k9.crypto.MessageCryptoStructureDetector;
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
import com.fsck.k9.mail.BodyPart;
|
import com.fsck.k9.mail.BodyPart;
|
||||||
|
@ -52,7 +52,6 @@ import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSink;
|
||||||
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource;
|
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource;
|
||||||
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
||||||
import org.openintents.openpgp.util.OpenPgpServiceConnection.OnBound;
|
import org.openintents.openpgp.util.OpenPgpServiceConnection.OnBound;
|
||||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,6 +83,7 @@ public class MessageCryptoHelper {
|
||||||
private State state;
|
private State state;
|
||||||
private CancelableBackgroundOperation cancelableBackgroundOperation;
|
private CancelableBackgroundOperation cancelableBackgroundOperation;
|
||||||
private boolean isCancelled;
|
private boolean isCancelled;
|
||||||
|
private boolean processSignedOnly;
|
||||||
|
|
||||||
private OpenPgpApi openPgpApi;
|
private OpenPgpApi openPgpApi;
|
||||||
private OpenPgpServiceConnection openPgpServiceConnection;
|
private OpenPgpServiceConnection openPgpServiceConnection;
|
||||||
|
@ -108,7 +108,7 @@ public class MessageCryptoHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void asyncStartOrResumeProcessingMessage(Message message, MessageCryptoCallback callback,
|
public void asyncStartOrResumeProcessingMessage(Message message, MessageCryptoCallback callback,
|
||||||
OpenPgpDecryptionResult cachedDecryptionResult) {
|
OpenPgpDecryptionResult cachedDecryptionResult, boolean processSignedOnly) {
|
||||||
if (this.currentMessage != null) {
|
if (this.currentMessage != null) {
|
||||||
reattachCallback(message, callback);
|
reattachCallback(message, callback);
|
||||||
return;
|
return;
|
||||||
|
@ -119,21 +119,72 @@ public class MessageCryptoHelper {
|
||||||
this.currentMessage = message;
|
this.currentMessage = message;
|
||||||
this.cachedDecryptionResult = cachedDecryptionResult;
|
this.cachedDecryptionResult = cachedDecryptionResult;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
this.processSignedOnly = processSignedOnly;
|
||||||
|
|
||||||
nextStep();
|
nextStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findPartsForEncryptionPass() {
|
private void findPartsForMultipartEncryptionPass() {
|
||||||
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(currentMessage);
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(currentMessage);
|
||||||
processFoundEncryptedParts(encryptedParts);
|
for (Part part : encryptedParts) {
|
||||||
|
if (!MessageHelper.isCompletePartAvailable(part)) {
|
||||||
|
addErrorAnnotation(part, CryptoError.OPENPGP_ENCRYPTED_BUT_INCOMPLETE, MessageHelper.createEmptyPart());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (MessageCryptoStructureDetector.isMultipartEncryptedOpenPgpProtocol(part)) {
|
||||||
|
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_ENCRYPTED, part);
|
||||||
|
partsToProcess.add(cryptoPart);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addErrorAnnotation(part, CryptoError.ENCRYPTED_BUT_UNSUPPORTED, MessageHelper.createEmptyPart());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findPartsForSignaturePass() {
|
private void findPartsForMultipartSignaturePass() {
|
||||||
List<Part> signedParts = MessageDecryptVerifier.findSignedParts(currentMessage, messageAnnotations);
|
List<Part> signedParts = MessageCryptoStructureDetector
|
||||||
processFoundSignedParts(signedParts);
|
.findMultipartSignedParts(currentMessage, messageAnnotations);
|
||||||
|
for (Part part : signedParts) {
|
||||||
|
if (!processSignedOnly) {
|
||||||
|
boolean isEncapsulatedSignature =
|
||||||
|
messageAnnotations.findKeyForAnnotationWithReplacementPart(part) != null;
|
||||||
|
if (!isEncapsulatedSignature) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!MessageHelper.isCompletePartAvailable(part)) {
|
||||||
|
MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part);
|
||||||
|
addErrorAnnotation(part, CryptoError.OPENPGP_SIGNED_BUT_INCOMPLETE, replacementPart);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (MessageCryptoStructureDetector.isMultipartSignedOpenPgpProtocol(part)) {
|
||||||
|
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_SIGNED, part);
|
||||||
|
partsToProcess.add(cryptoPart);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part);
|
||||||
|
addErrorAnnotation(part, CryptoError.SIGNED_BUT_UNSUPPORTED, replacementPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(currentMessage);
|
private void findPartsForPgpInlinePass() {
|
||||||
processFoundInlinePgpParts(inlineParts);
|
List<Part> inlineParts = MessageCryptoStructureDetector.findPgpInlineParts(currentMessage);
|
||||||
|
for (Part part : inlineParts) {
|
||||||
|
if (!processSignedOnly && !MessageCryptoStructureDetector.isPartPgpInlineEncrypted(part)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentMessage.getFlags().contains(Flag.X_DOWNLOADED_FULL)) {
|
||||||
|
if (MessageCryptoStructureDetector.isPartPgpInlineEncrypted(part)) {
|
||||||
|
addErrorAnnotation(part, CryptoError.OPENPGP_ENCRYPTED_BUT_INCOMPLETE, NO_REPLACEMENT_PART);
|
||||||
|
} else {
|
||||||
|
addErrorAnnotation(part, CryptoError.OPENPGP_SIGNED_BUT_INCOMPLETE, NO_REPLACEMENT_PART);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_INLINE, part);
|
||||||
|
partsToProcess.add(cryptoPart);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findPartsForAutocryptPass() {
|
private void findPartsForAutocryptPass() {
|
||||||
|
@ -148,60 +199,11 @@ public class MessageCryptoHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processFoundEncryptedParts(List<Part> foundParts) {
|
|
||||||
for (Part part : foundParts) {
|
|
||||||
if (!MessageHelper.isCompletePartAvailable(part)) {
|
|
||||||
addErrorAnnotation(part, CryptoError.OPENPGP_ENCRYPTED_BUT_INCOMPLETE, MessageHelper.createEmptyPart());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) {
|
|
||||||
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_ENCRYPTED, part);
|
|
||||||
partsToProcess.add(cryptoPart);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
addErrorAnnotation(part, CryptoError.ENCRYPTED_BUT_UNSUPPORTED, MessageHelper.createEmptyPart());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processFoundSignedParts(List<Part> foundParts) {
|
|
||||||
for (Part part : foundParts) {
|
|
||||||
if (!MessageHelper.isCompletePartAvailable(part)) {
|
|
||||||
MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part);
|
|
||||||
addErrorAnnotation(part, CryptoError.OPENPGP_SIGNED_BUT_INCOMPLETE, replacementPart);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) {
|
|
||||||
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_SIGNED, part);
|
|
||||||
partsToProcess.add(cryptoPart);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part);
|
|
||||||
addErrorAnnotation(part, CryptoError.SIGNED_BUT_UNSUPPORTED, replacementPart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addErrorAnnotation(Part part, CryptoError error, MimeBodyPart replacementPart) {
|
private void addErrorAnnotation(Part part, CryptoError error, MimeBodyPart replacementPart) {
|
||||||
CryptoResultAnnotation annotation = CryptoResultAnnotation.createErrorAnnotation(error, replacementPart);
|
CryptoResultAnnotation annotation = CryptoResultAnnotation.createErrorAnnotation(error, replacementPart);
|
||||||
messageAnnotations.put(part, annotation);
|
messageAnnotations.put(part, annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processFoundInlinePgpParts(List<Part> foundParts) {
|
|
||||||
for (Part part : foundParts) {
|
|
||||||
if (!currentMessage.getFlags().contains(Flag.X_DOWNLOADED_FULL)) {
|
|
||||||
if (MessageDecryptVerifier.isPartPgpInlineEncrypted(part)) {
|
|
||||||
addErrorAnnotation(part, CryptoError.OPENPGP_ENCRYPTED_BUT_INCOMPLETE, NO_REPLACEMENT_PART);
|
|
||||||
} else {
|
|
||||||
MimeBodyPart replacementPart = extractClearsignedTextReplacementPart(part);
|
|
||||||
addErrorAnnotation(part, CryptoError.OPENPGP_SIGNED_BUT_INCOMPLETE, replacementPart);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_INLINE, part);
|
|
||||||
partsToProcess.add(cryptoPart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void nextStep() {
|
private void nextStep() {
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
|
@ -396,7 +398,7 @@ public class MessageCryptoHelper {
|
||||||
private void callAsyncDetachedVerify(Intent intent) throws IOException, MessagingException {
|
private void callAsyncDetachedVerify(Intent intent) throws IOException, MessagingException {
|
||||||
OpenPgpDataSource dataSource = getDataSourceForSignedData(currentCryptoPart.part);
|
OpenPgpDataSource dataSource = getDataSourceForSignedData(currentCryptoPart.part);
|
||||||
|
|
||||||
byte[] signatureData = MessageDecryptVerifier.getSignatureData(currentCryptoPart.part);
|
byte[] signatureData = MessageCryptoStructureDetector.getSignatureData(currentCryptoPart.part);
|
||||||
intent.putExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE, signatureData);
|
intent.putExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE, signatureData);
|
||||||
|
|
||||||
openPgpApi.executeApiAsync(intent, dataSource, new IOpenPgpSinkResultCallback<Void>() {
|
openPgpApi.executeApiAsync(intent, dataSource, new IOpenPgpSinkResultCallback<Void>() {
|
||||||
|
@ -645,17 +647,19 @@ public class MessageCryptoHelper {
|
||||||
case START: {
|
case START: {
|
||||||
state = State.ENCRYPTION;
|
state = State.ENCRYPTION;
|
||||||
|
|
||||||
findPartsForEncryptionPass();
|
findPartsForMultipartEncryptionPass();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ENCRYPTION: {
|
case ENCRYPTION: {
|
||||||
state = State.SIGNATURES;
|
state = State.SIGNATURES_AND_INLINE;
|
||||||
|
|
||||||
findPartsForSignaturePass();
|
findPartsForMultipartSignaturePass();
|
||||||
|
findPartsForPgpInlinePass();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case SIGNATURES: {
|
|
||||||
|
case SIGNATURES_AND_INLINE: {
|
||||||
state = State.AUTOCRYPT;
|
state = State.AUTOCRYPT;
|
||||||
|
|
||||||
findPartsForAutocryptPass();
|
findPartsForAutocryptPass();
|
||||||
|
@ -780,22 +784,7 @@ public class MessageCryptoHelper {
|
||||||
return replacementPart;
|
return replacementPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MimeBodyPart extractClearsignedTextReplacementPart(Part part) {
|
|
||||||
try {
|
|
||||||
String clearsignedText = MessageExtractor.getTextFromPart(part);
|
|
||||||
String replacementText = OpenPgpUtils.extractClearsignedMessage(clearsignedText);
|
|
||||||
if (replacementText == null) {
|
|
||||||
Timber.e("failed to extract clearsigned text for replacement part");
|
|
||||||
return NO_REPLACEMENT_PART;
|
|
||||||
}
|
|
||||||
return new MimeBodyPart(new TextBody(replacementText), "text/plain");
|
|
||||||
} catch (MessagingException e) {
|
|
||||||
Timber.e(e, "failed to create clearsigned text replacement part");
|
|
||||||
return NO_REPLACEMENT_PART;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum State {
|
private enum State {
|
||||||
START, ENCRYPTION, SIGNATURES, AUTOCRYPT, FINISHED
|
START, ENCRYPTION, SIGNATURES_AND_INLINE, AUTOCRYPT, FINISHED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import java.util.List;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import com.fsck.k9.crypto.MessageDecryptVerifier;
|
import com.fsck.k9.crypto.MessageCryptoStructureDetector;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
import com.fsck.k9.mailstore.CryptoResultAnnotation;
|
import com.fsck.k9.mailstore.CryptoResultAnnotation;
|
||||||
|
@ -21,7 +21,7 @@ public class MessageCryptoSplitter {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static CryptoMessageParts split(@NonNull Message message, @Nullable MessageCryptoAnnotations annotations) {
|
public static CryptoMessageParts split(@NonNull Message message, @Nullable MessageCryptoAnnotations annotations) {
|
||||||
ArrayList<Part> extraParts = new ArrayList<>();
|
ArrayList<Part> extraParts = new ArrayList<>();
|
||||||
Part primaryPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, extraParts);
|
Part primaryPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, extraParts);
|
||||||
if (primaryPart == null) {
|
if (primaryPart == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,20 +7,19 @@ import java.util.List;
|
||||||
import com.fsck.k9.K9RobolectricTestRunner;
|
import com.fsck.k9.K9RobolectricTestRunner;
|
||||||
import com.fsck.k9.mail.BodyPart;
|
import com.fsck.k9.mail.BodyPart;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
|
||||||
import com.fsck.k9.mail.Multipart;
|
import com.fsck.k9.mail.Multipart;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
import com.fsck.k9.mail.internet.MimeBodyPart;
|
|
||||||
import com.fsck.k9.mail.internet.MimeHeader;
|
|
||||||
import com.fsck.k9.mail.internet.MimeMessage;
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
import com.fsck.k9.mail.internet.MimeMessageHelper;
|
import com.fsck.k9.mail.internet.MimeMessageHelper;
|
||||||
import com.fsck.k9.mail.internet.MimeMultipart;
|
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
|
import com.fsck.k9.ui.crypto.MessageCryptoAnnotations;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
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 org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
@ -29,13 +28,12 @@ import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
@RunWith(K9RobolectricTestRunner.class)
|
@RunWith(K9RobolectricTestRunner.class)
|
||||||
@Config(manifest = Config.NONE)
|
@Config(manifest = Config.NONE)
|
||||||
public class MessageDecryptVerifierTest {
|
public class MessageCryptoStructureDetectorTest {
|
||||||
private static final String MIME_TYPE_MULTIPART_ENCRYPTED = "multipart/encrypted";
|
MessageCryptoAnnotations messageCryptoAnnotations = mock(MessageCryptoAnnotations.class);
|
||||||
private MessageCryptoAnnotations messageCryptoAnnotations = mock(MessageCryptoAnnotations.class);
|
static final String PGP_INLINE_DATA = "" +
|
||||||
private static final String PROTCOL_PGP_ENCRYPTED = "application/pgp-encrypted";
|
|
||||||
private static final String PGP_INLINE_DATA = "" +
|
|
||||||
"-----BEGIN PGP MESSAGE-----\n" +
|
"-----BEGIN PGP MESSAGE-----\n" +
|
||||||
"Header: Value\n" +
|
"Header: Value\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -49,7 +47,7 @@ public class MessageDecryptVerifierTest {
|
||||||
Message message = new MimeMessage();
|
Message message = new MimeMessage();
|
||||||
MimeMessageHelper.setBody(message, new TextBody(PGP_INLINE_DATA));
|
MimeMessageHelper.setBody(message, new TextBody(PGP_INLINE_DATA));
|
||||||
|
|
||||||
Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
Part cryptoPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
||||||
|
|
||||||
assertSame(message, cryptoPart);
|
assertSame(message, cryptoPart);
|
||||||
}
|
}
|
||||||
|
@ -60,12 +58,12 @@ public class MessageDecryptVerifierTest {
|
||||||
BodyPart pgpInlinePart = bodypart("text/plain", PGP_INLINE_DATA);
|
BodyPart pgpInlinePart = bodypart("text/plain", PGP_INLINE_DATA);
|
||||||
Message message = messageFromBody(
|
Message message = messageFromBody(
|
||||||
multipart("alternative",
|
multipart("alternative",
|
||||||
pgpInlinePart,
|
pgpInlinePart,
|
||||||
bodypart("text/html")
|
bodypart("text/html")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
Part cryptoPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
||||||
|
|
||||||
assertSame(pgpInlinePart, cryptoPart);
|
assertSame(pgpInlinePart, cryptoPart);
|
||||||
}
|
}
|
||||||
|
@ -81,7 +79,7 @@ public class MessageDecryptVerifierTest {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
Part cryptoPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
||||||
|
|
||||||
assertSame(pgpInlinePart, cryptoPart);
|
assertSame(pgpInlinePart, cryptoPart);
|
||||||
}
|
}
|
||||||
|
@ -101,7 +99,7 @@ public class MessageDecryptVerifierTest {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
Part cryptoPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
||||||
|
|
||||||
assertSame(pgpInlinePart, cryptoPart);
|
assertSame(pgpInlinePart, cryptoPart);
|
||||||
}
|
}
|
||||||
|
@ -113,7 +111,7 @@ public class MessageDecryptVerifierTest {
|
||||||
multipart("alternative")
|
multipart("alternative")
|
||||||
);
|
);
|
||||||
|
|
||||||
Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
Part cryptoPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
||||||
|
|
||||||
assertNull(cryptoPart);
|
assertNull(cryptoPart);
|
||||||
}
|
}
|
||||||
|
@ -125,7 +123,7 @@ public class MessageDecryptVerifierTest {
|
||||||
multipart("mixed")
|
multipart("mixed")
|
||||||
);
|
);
|
||||||
|
|
||||||
Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
Part cryptoPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
||||||
|
|
||||||
assertNull(cryptoPart);
|
assertNull(cryptoPart);
|
||||||
}
|
}
|
||||||
|
@ -140,7 +138,7 @@ public class MessageDecryptVerifierTest {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
Part cryptoPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);
|
||||||
|
|
||||||
assertNull(cryptoPart);
|
assertNull(cryptoPart);
|
||||||
}
|
}
|
||||||
|
@ -149,7 +147,7 @@ public class MessageDecryptVerifierTest {
|
||||||
public void findEncryptedPartsShouldReturnEmptyListForEmptyMessage() throws Exception {
|
public void findEncryptedPartsShouldReturnEmptyListForEmptyMessage() throws Exception {
|
||||||
MimeMessage emptyMessage = new MimeMessage();
|
MimeMessage emptyMessage = new MimeMessage();
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(emptyMessage);
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(emptyMessage);
|
||||||
|
|
||||||
assertEquals(0, encryptedParts.size());
|
assertEquals(0, encryptedParts.size());
|
||||||
}
|
}
|
||||||
|
@ -159,56 +157,42 @@ public class MessageDecryptVerifierTest {
|
||||||
MimeMessage message = new MimeMessage();
|
MimeMessage message = new MimeMessage();
|
||||||
message.setBody(new TextBody("message text"));
|
message.setBody(new TextBody("message text"));
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(message);
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(message);
|
||||||
|
|
||||||
assertEquals(0, encryptedParts.size());
|
assertEquals(0, encryptedParts.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findEncryptedPartsShouldReturnEmptyEncryptedPart() throws Exception {
|
public void findEncrypted__withMultipartEncrypted__shouldReturnRoot() throws Exception {
|
||||||
MimeMessage message = new MimeMessage();
|
Message message = messageFromBody(
|
||||||
MimeMultipart multipartEncrypted = MimeMultipart.newInstance();
|
multipart("encrypted", "protocol=\"application/pgp-encrypted\"",
|
||||||
multipartEncrypted.setSubType("encrypted");
|
bodypart("application/pgp-encrypted"),
|
||||||
MimeMessageHelper.setBody(message, multipartEncrypted);
|
bodypart("application/octet-stream")
|
||||||
setContentTypeWithProtocol(message, MIME_TYPE_MULTIPART_ENCRYPTED, PROTCOL_PGP_ENCRYPTED);
|
)
|
||||||
|
);
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(message);
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(message);
|
||||||
|
|
||||||
assertEquals(1, encryptedParts.size());
|
assertEquals(1, encryptedParts.size());
|
||||||
assertSame(message, encryptedParts.get(0));
|
assertSame(message, encryptedParts.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findEncryptedPartsShouldReturnMultipleEncryptedParts() throws Exception {
|
public void findEncrypted__withBadProtocol__shouldReturnEmpty() throws Exception {
|
||||||
MimeMessage message = new MimeMessage();
|
Message message = messageFromBody(
|
||||||
MimeMultipart multipartMixed = MimeMultipart.newInstance();
|
multipart("encrypted", "protocol=\"application/not-pgp-encrypted\"",
|
||||||
multipartMixed.setSubType("mixed");
|
bodypart("application/pgp-encrypted"),
|
||||||
MimeMessageHelper.setBody(message, multipartMixed);
|
bodypart("application/octet-stream", "content")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
MimeMultipart multipartEncryptedOne = MimeMultipart.newInstance();
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(message);
|
||||||
multipartEncryptedOne.setSubType("encrypted");
|
|
||||||
MimeBodyPart bodyPartOne = new MimeBodyPart(multipartEncryptedOne);
|
|
||||||
setContentTypeWithProtocol(bodyPartOne, MIME_TYPE_MULTIPART_ENCRYPTED, PROTCOL_PGP_ENCRYPTED);
|
|
||||||
multipartMixed.addBodyPart(bodyPartOne);
|
|
||||||
|
|
||||||
MimeBodyPart bodyPartTwo = new MimeBodyPart(null, "text/plain");
|
assertTrue(encryptedParts.isEmpty());
|
||||||
multipartMixed.addBodyPart(bodyPartTwo);
|
|
||||||
|
|
||||||
MimeMultipart multipartEncryptedThree = MimeMultipart.newInstance();
|
|
||||||
multipartEncryptedThree.setSubType("encrypted");
|
|
||||||
MimeBodyPart bodyPartThree = new MimeBodyPart(multipartEncryptedThree);
|
|
||||||
setContentTypeWithProtocol(bodyPartThree, MIME_TYPE_MULTIPART_ENCRYPTED, PROTCOL_PGP_ENCRYPTED);
|
|
||||||
multipartMixed.addBodyPart(bodyPartThree);
|
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(message);
|
|
||||||
|
|
||||||
assertEquals(2, encryptedParts.size());
|
|
||||||
assertSame(bodyPartOne, encryptedParts.get(0));
|
|
||||||
assertSame(bodyPartThree, encryptedParts.get(1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findEncrypted__withMultipartEncrypted__shouldReturnRoot() throws Exception {
|
public void findEncrypted__withBadProtocolAndNoBody__shouldReturnRoot() throws Exception {
|
||||||
Message message = messageFromBody(
|
Message message = messageFromBody(
|
||||||
multipart("encrypted",
|
multipart("encrypted",
|
||||||
bodypart("application/pgp-encrypted"),
|
bodypart("application/pgp-encrypted"),
|
||||||
|
@ -216,24 +200,64 @@ public class MessageDecryptVerifierTest {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(message);
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(message);
|
||||||
|
|
||||||
assertEquals(1, encryptedParts.size());
|
assertEquals(1, encryptedParts.size());
|
||||||
assertSame(message, encryptedParts.get(0));
|
assertSame(message, encryptedParts.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findEncrypted__withEmptyProtocol__shouldReturnEmpty() throws Exception {
|
||||||
|
Message message = messageFromBody(
|
||||||
|
multipart("encrypted",
|
||||||
|
bodypart("application/pgp-encrypted"),
|
||||||
|
bodypart("application/octet-stream", "content")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(message);
|
||||||
|
|
||||||
|
assertTrue(encryptedParts.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findEncrypted__withMissingEncryptedBody__shouldReturnEmpty() throws Exception {
|
||||||
|
Message message = messageFromBody(
|
||||||
|
multipart("encrypted", "protocol=\"application/pgp-encrypted\"",
|
||||||
|
bodypart("application/pgp-encrypted")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(message);
|
||||||
|
|
||||||
|
assertTrue(encryptedParts.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findEncrypted__withBadStructure__shouldReturnEmpty() throws Exception {
|
||||||
|
Message message = messageFromBody(
|
||||||
|
multipart("encrypted", "protocol=\"application/pgp-encrypted\"",
|
||||||
|
bodypart("application/octet-stream")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(message);
|
||||||
|
|
||||||
|
assertTrue(encryptedParts.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findEncrypted__withMultipartMixedSubEncrypted__shouldReturnRoot() throws Exception {
|
public void findEncrypted__withMultipartMixedSubEncrypted__shouldReturnRoot() throws Exception {
|
||||||
Message message = messageFromBody(
|
Message message = messageFromBody(
|
||||||
multipart("mixed",
|
multipart("mixed",
|
||||||
multipart("encrypted",
|
multipart("encrypted", "protocol=\"application/pgp-encrypted\"",
|
||||||
bodypart("application/pgp-encrypted"),
|
bodypart("application/pgp-encrypted"),
|
||||||
bodypart("application/octet-stream")
|
bodypart("application/octet-stream")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(message);
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(message);
|
||||||
|
|
||||||
assertEquals(1, encryptedParts.size());
|
assertEquals(1, encryptedParts.size());
|
||||||
assertSame(getPart(message, 0), encryptedParts.get(0));
|
assertSame(getPart(message, 0), encryptedParts.get(0));
|
||||||
|
@ -244,18 +268,18 @@ public class MessageDecryptVerifierTest {
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Message message = messageFromBody(
|
Message message = messageFromBody(
|
||||||
multipart("mixed",
|
multipart("mixed",
|
||||||
multipart("encrypted",
|
multipart("encrypted", "protocol=\"application/pgp-encrypted\"",
|
||||||
bodypart("application/pgp-encrypted"),
|
bodypart("application/pgp-encrypted"),
|
||||||
bodypart("application/octet-stream")
|
bodypart("application/octet-stream")
|
||||||
),
|
),
|
||||||
multipart("encrypted",
|
multipart("encrypted", "protocol=\"application/pgp-encrypted\"",
|
||||||
bodypart("application/pgp-encrypted"),
|
bodypart("application/pgp-encrypted"),
|
||||||
bodypart("application/octet-stream")
|
bodypart("application/octet-stream")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(message);
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(message);
|
||||||
|
|
||||||
assertEquals(2, encryptedParts.size());
|
assertEquals(2, encryptedParts.size());
|
||||||
assertSame(getPart(message, 0), encryptedParts.get(0));
|
assertSame(getPart(message, 0), encryptedParts.get(0));
|
||||||
|
@ -267,14 +291,14 @@ public class MessageDecryptVerifierTest {
|
||||||
Message message = messageFromBody(
|
Message message = messageFromBody(
|
||||||
multipart("mixed",
|
multipart("mixed",
|
||||||
bodypart("text/plain"),
|
bodypart("text/plain"),
|
||||||
multipart("encrypted",
|
multipart("encrypted", "protocol=\"application/pgp-encrypted\"",
|
||||||
bodypart("application/pgp-encrypted"),
|
bodypart("application/pgp-encrypted"),
|
||||||
bodypart("application/octet-stream")
|
bodypart("application/octet-stream")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(message);
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(message);
|
||||||
|
|
||||||
assertEquals(1, encryptedParts.size());
|
assertEquals(1, encryptedParts.size());
|
||||||
assertSame(getPart(message, 1), encryptedParts.get(0));
|
assertSame(getPart(message, 1), encryptedParts.get(0));
|
||||||
|
@ -284,7 +308,7 @@ public class MessageDecryptVerifierTest {
|
||||||
public void findEncrypted__withMultipartMixedSubEncryptedAndText__shouldReturnEncrypted() throws Exception {
|
public void findEncrypted__withMultipartMixedSubEncryptedAndText__shouldReturnEncrypted() throws Exception {
|
||||||
Message message = messageFromBody(
|
Message message = messageFromBody(
|
||||||
multipart("mixed",
|
multipart("mixed",
|
||||||
multipart("encrypted",
|
multipart("encrypted", "protocol=\"application/pgp-encrypted\"",
|
||||||
bodypart("application/pgp-encrypted"),
|
bodypart("application/pgp-encrypted"),
|
||||||
bodypart("application/octet-stream")
|
bodypart("application/octet-stream")
|
||||||
),
|
),
|
||||||
|
@ -292,7 +316,7 @@ public class MessageDecryptVerifierTest {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(message);
|
List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(message);
|
||||||
|
|
||||||
assertEquals(1, encryptedParts.size());
|
assertEquals(1, encryptedParts.size());
|
||||||
assertSame(getPart(message, 0), encryptedParts.get(0));
|
assertSame(getPart(message, 0), encryptedParts.get(0));
|
||||||
|
@ -301,22 +325,83 @@ public class MessageDecryptVerifierTest {
|
||||||
@Test
|
@Test
|
||||||
public void findSigned__withSimpleMultipartSigned__shouldReturnRoot() throws Exception {
|
public void findSigned__withSimpleMultipartSigned__shouldReturnRoot() throws Exception {
|
||||||
Message message = messageFromBody(
|
Message message = messageFromBody(
|
||||||
multipart("signed",
|
multipart("signed", "protocol=\"application/pgp-signature\"",
|
||||||
bodypart("text/plain"),
|
bodypart("text/plain"),
|
||||||
bodypart("application/pgp-signature")
|
bodypart("application/pgp-signature")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations);
|
List<Part> signedParts = MessageCryptoStructureDetector
|
||||||
|
.findMultipartSignedParts(message, messageCryptoAnnotations);
|
||||||
|
|
||||||
assertEquals(1, signedParts.size());
|
assertEquals(1, signedParts.size());
|
||||||
assertSame(message, signedParts.get(0));
|
assertSame(message, signedParts.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findSigned__withComplexMultipartSigned__shouldReturnRoot() throws Exception {
|
public void findSigned__withNoProtocolAndNoBody__shouldReturnRoot() throws Exception {
|
||||||
Message message = messageFromBody(
|
Message message = messageFromBody(
|
||||||
multipart("signed",
|
multipart("signed",
|
||||||
|
bodypart("text/plain"),
|
||||||
|
bodypart("application/pgp-signature")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Part> signedParts = MessageCryptoStructureDetector
|
||||||
|
.findMultipartSignedParts(message, messageCryptoAnnotations);
|
||||||
|
|
||||||
|
assertEquals(1, signedParts.size());
|
||||||
|
assertSame(message, signedParts.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findSigned__withBadProtocol__shouldReturnNothing() throws Exception {
|
||||||
|
Message message = messageFromBody(
|
||||||
|
multipart("signed", "protocol=\"application/not-pgp-signature\"",
|
||||||
|
bodypart("text/plain", "content"),
|
||||||
|
bodypart("application/pgp-signature")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Part> signedParts = MessageCryptoStructureDetector
|
||||||
|
.findMultipartSignedParts(message, messageCryptoAnnotations);
|
||||||
|
|
||||||
|
assertTrue(signedParts.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findSigned__withEmptyProtocol__shouldReturnRoot() throws Exception {
|
||||||
|
Message message = messageFromBody(
|
||||||
|
multipart("signed",
|
||||||
|
bodypart("text/plain", "content"),
|
||||||
|
bodypart("application/pgp-signature")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Part> signedParts = MessageCryptoStructureDetector
|
||||||
|
.findMultipartSignedParts(message, messageCryptoAnnotations);
|
||||||
|
|
||||||
|
assertTrue(signedParts.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findSigned__withMissingSignature__shouldReturnEmpty() throws Exception {
|
||||||
|
Message message = messageFromBody(
|
||||||
|
multipart("signed", "protocol=\"application/pgp-signature\"",
|
||||||
|
bodypart("text/plain")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Part> signedParts = MessageCryptoStructureDetector
|
||||||
|
.findMultipartSignedParts(message, messageCryptoAnnotations);
|
||||||
|
|
||||||
|
assertTrue(signedParts.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findSigned__withComplexMultipartSigned__shouldReturnRoot() throws Exception {
|
||||||
|
Message message = messageFromBody(
|
||||||
|
multipart("signed", "protocol=\"application/pgp-signature\"",
|
||||||
multipart("mixed",
|
multipart("mixed",
|
||||||
bodypart("text/plain"),
|
bodypart("text/plain"),
|
||||||
bodypart("application/pdf")
|
bodypart("application/pdf")
|
||||||
|
@ -325,7 +410,8 @@ public class MessageDecryptVerifierTest {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations);
|
List<Part> signedParts = MessageCryptoStructureDetector
|
||||||
|
.findMultipartSignedParts(message, messageCryptoAnnotations);
|
||||||
|
|
||||||
assertEquals(1, signedParts.size());
|
assertEquals(1, signedParts.size());
|
||||||
assertSame(message, signedParts.get(0));
|
assertSame(message, signedParts.get(0));
|
||||||
|
@ -335,14 +421,15 @@ public class MessageDecryptVerifierTest {
|
||||||
public void findEncrypted__withMultipartMixedSubSigned__shouldReturnSigned() throws Exception {
|
public void findEncrypted__withMultipartMixedSubSigned__shouldReturnSigned() throws Exception {
|
||||||
Message message = messageFromBody(
|
Message message = messageFromBody(
|
||||||
multipart("mixed",
|
multipart("mixed",
|
||||||
multipart("signed",
|
multipart("signed", "protocol=\"application/pgp-signature\"",
|
||||||
bodypart("text/plain"),
|
bodypart("text/plain"),
|
||||||
bodypart("application/pgp-signature")
|
bodypart("application/pgp-signature")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations);
|
List<Part> signedParts = MessageCryptoStructureDetector
|
||||||
|
.findMultipartSignedParts(message, messageCryptoAnnotations);
|
||||||
|
|
||||||
assertEquals(1, signedParts.size());
|
assertEquals(1, signedParts.size());
|
||||||
assertSame(getPart(message, 0), signedParts.get(0));
|
assertSame(getPart(message, 0), signedParts.get(0));
|
||||||
|
@ -352,7 +439,7 @@ public class MessageDecryptVerifierTest {
|
||||||
public void findEncrypted__withMultipartMixedSubSignedAndText__shouldReturnSigned() throws Exception {
|
public void findEncrypted__withMultipartMixedSubSignedAndText__shouldReturnSigned() throws Exception {
|
||||||
Message message = messageFromBody(
|
Message message = messageFromBody(
|
||||||
multipart("mixed",
|
multipart("mixed",
|
||||||
multipart("signed",
|
multipart("signed", "application/pgp-signature",
|
||||||
bodypart("text/plain"),
|
bodypart("text/plain"),
|
||||||
bodypart("application/pgp-signature")
|
bodypart("application/pgp-signature")
|
||||||
),
|
),
|
||||||
|
@ -360,7 +447,8 @@ public class MessageDecryptVerifierTest {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations);
|
List<Part> signedParts = MessageCryptoStructureDetector
|
||||||
|
.findMultipartSignedParts(message, messageCryptoAnnotations);
|
||||||
|
|
||||||
assertEquals(1, signedParts.size());
|
assertEquals(1, signedParts.size());
|
||||||
assertSame(getPart(message, 0), signedParts.get(0));
|
assertSame(getPart(message, 0), signedParts.get(0));
|
||||||
|
@ -371,14 +459,15 @@ public class MessageDecryptVerifierTest {
|
||||||
Message message = messageFromBody(
|
Message message = messageFromBody(
|
||||||
multipart("mixed",
|
multipart("mixed",
|
||||||
bodypart("text/plain"),
|
bodypart("text/plain"),
|
||||||
multipart("signed",
|
multipart("signed", "application/pgp-signature",
|
||||||
bodypart("text/plain"),
|
bodypart("text/plain"),
|
||||||
bodypart("application/pgp-signature")
|
bodypart("application/pgp-signature")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations);
|
List<Part> signedParts = MessageCryptoStructureDetector
|
||||||
|
.findMultipartSignedParts(message, messageCryptoAnnotations);
|
||||||
|
|
||||||
assertEquals(1, signedParts.size());
|
assertEquals(1, signedParts.size());
|
||||||
assertSame(getPart(message, 1), signedParts.get(0));
|
assertSame(getPart(message, 1), signedParts.get(0));
|
||||||
|
@ -395,7 +484,7 @@ public class MessageDecryptVerifierTest {
|
||||||
MimeMessage message = new MimeMessage();
|
MimeMessage message = new MimeMessage();
|
||||||
message.setBody(new TextBody(pgpInlineData));
|
message.setBody(new TextBody(pgpInlineData));
|
||||||
|
|
||||||
assertTrue(MessageDecryptVerifier.isPartPgpInlineEncrypted(message));
|
assertTrue(MessageCryptoStructureDetector.isPartPgpInlineEncrypted(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -410,8 +499,8 @@ public class MessageDecryptVerifierTest {
|
||||||
MimeMessage message = new MimeMessage();
|
MimeMessage message = new MimeMessage();
|
||||||
message.setBody(new TextBody(pgpInlineData));
|
message.setBody(new TextBody(pgpInlineData));
|
||||||
|
|
||||||
assertTrue(MessageDecryptVerifier.isPartPgpInlineEncryptedOrSigned(message));
|
assertTrue(MessageCryptoStructureDetector.isPartPgpInlineEncryptedOrSigned(message));
|
||||||
assertTrue(MessageDecryptVerifier.isPartPgpInlineEncrypted(message));
|
assertTrue(MessageCryptoStructureDetector.isPartPgpInlineEncrypted(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -426,8 +515,8 @@ public class MessageDecryptVerifierTest {
|
||||||
MimeMessage message = new MimeMessage();
|
MimeMessage message = new MimeMessage();
|
||||||
message.setBody(new TextBody(pgpInlineData));
|
message.setBody(new TextBody(pgpInlineData));
|
||||||
|
|
||||||
assertFalse(MessageDecryptVerifier.isPartPgpInlineEncryptedOrSigned(message));
|
assertFalse(MessageCryptoStructureDetector.isPartPgpInlineEncryptedOrSigned(message));
|
||||||
assertFalse(MessageDecryptVerifier.isPartPgpInlineEncrypted(message));
|
assertFalse(MessageCryptoStructureDetector.isPartPgpInlineEncrypted(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -444,7 +533,7 @@ public class MessageDecryptVerifierTest {
|
||||||
MimeMessage message = new MimeMessage();
|
MimeMessage message = new MimeMessage();
|
||||||
message.setBody(new TextBody(pgpInlineData));
|
message.setBody(new TextBody(pgpInlineData));
|
||||||
|
|
||||||
assertTrue(MessageDecryptVerifier.isPartPgpInlineEncryptedOrSigned(message));
|
assertTrue(MessageCryptoStructureDetector.isPartPgpInlineEncryptedOrSigned(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -461,44 +550,14 @@ public class MessageDecryptVerifierTest {
|
||||||
MimeMessage message = new MimeMessage();
|
MimeMessage message = new MimeMessage();
|
||||||
message.setBody(new TextBody(pgpInlineData));
|
message.setBody(new TextBody(pgpInlineData));
|
||||||
|
|
||||||
assertFalse(MessageDecryptVerifier.isPartPgpInlineEncrypted(message));
|
assertFalse(MessageCryptoStructureDetector.isPartPgpInlineEncrypted(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
MimeMessage messageFromBody(BodyPart bodyPart) throws MessagingException {
|
static Part getPart(Part searchRootPart, int... indexes) {
|
||||||
MimeMessage message = new MimeMessage();
|
|
||||||
MimeMessageHelper.setBody(message, bodyPart.getBody());
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
MimeBodyPart multipart(String type, BodyPart... subParts) throws MessagingException {
|
|
||||||
MimeMultipart multiPart = MimeMultipart.newInstance();
|
|
||||||
multiPart.setSubType(type);
|
|
||||||
for (BodyPart subPart : subParts) {
|
|
||||||
multiPart.addBodyPart(subPart);
|
|
||||||
}
|
|
||||||
return new MimeBodyPart(multiPart);
|
|
||||||
}
|
|
||||||
|
|
||||||
BodyPart bodypart(String type) throws MessagingException {
|
|
||||||
return new MimeBodyPart(null, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
BodyPart bodypart(String type, String text) throws MessagingException {
|
|
||||||
TextBody textBody = new TextBody(text);
|
|
||||||
return new MimeBodyPart(textBody, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Part getPart(Part searchRootPart, int... indexes) {
|
|
||||||
Part part = searchRootPart;
|
Part part = searchRootPart;
|
||||||
for (int index : indexes) {
|
for (int index : indexes) {
|
||||||
part = ((Multipart) part.getBody()).getBodyPart(index);
|
part = ((Multipart) part.getBody()).getBodyPart(index);
|
||||||
}
|
}
|
||||||
return part;
|
return part;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Find a cleaner way to do this
|
|
||||||
private static void setContentTypeWithProtocol(Part part, String mimeType, String protocol)
|
|
||||||
throws MessagingException {
|
|
||||||
part.setHeader(MimeHeader.HEADER_CONTENT_TYPE, mimeType + "; protocol=\"" + protocol + "\"");
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.fsck.k9.message;
|
||||||
|
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.Body;
|
||||||
|
import com.fsck.k9.mail.BodyPart;
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
import com.fsck.k9.mail.internet.MimeBodyPart;
|
||||||
|
import com.fsck.k9.mail.internet.MimeHeader;
|
||||||
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
|
import com.fsck.k9.mail.internet.MimeMessageHelper;
|
||||||
|
import com.fsck.k9.mail.internet.MimeMultipart;
|
||||||
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
|
||||||
|
|
||||||
|
public class TestMessageConstructionUtils {
|
||||||
|
public static MimeMessage messageFromBody(BodyPart bodyPart) throws MessagingException {
|
||||||
|
MimeMessage message = new MimeMessage();
|
||||||
|
MimeMessageHelper.setBody(message, bodyPart.getBody());
|
||||||
|
if (bodyPart.getContentType() != null) {
|
||||||
|
message.setHeader("Content-Type", bodyPart.getContentType());
|
||||||
|
}
|
||||||
|
message.setUid("msguid");
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MimeBodyPart multipart(String type, BodyPart... subParts) throws MessagingException {
|
||||||
|
return multipart(type, null, subParts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MimeBodyPart multipart(String type, String typeParameters, BodyPart... subParts) throws MessagingException {
|
||||||
|
MimeMultipart multiPart = MimeMultipart.newInstance();
|
||||||
|
multiPart.setSubType(type);
|
||||||
|
for (BodyPart subPart : subParts) {
|
||||||
|
multiPart.addBodyPart(subPart);
|
||||||
|
}
|
||||||
|
MimeBodyPart mimeBodyPart = new MimeBodyPart(multiPart);
|
||||||
|
if (typeParameters != null) {
|
||||||
|
mimeBodyPart.setHeader(MimeHeader.HEADER_CONTENT_TYPE,
|
||||||
|
mimeBodyPart.getContentType() + "; " + typeParameters);
|
||||||
|
}
|
||||||
|
return mimeBodyPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BodyPart bodypart(String type) throws MessagingException {
|
||||||
|
return new MimeBodyPart(null, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BodyPart bodypart(String type, String text) throws MessagingException {
|
||||||
|
TextBody textBody = new TextBody(text);
|
||||||
|
return new MimeBodyPart(textBody, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BodyPart bodypart(String type, Body body) throws MessagingException {
|
||||||
|
return new MimeBodyPart(body, type);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,10 +14,8 @@ import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
import com.fsck.k9.mail.BodyPart;
|
import com.fsck.k9.mail.BodyPart;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.Multipart;
|
|
||||||
import com.fsck.k9.mail.internet.MimeBodyPart;
|
import com.fsck.k9.mail.internet.MimeBodyPart;
|
||||||
import com.fsck.k9.mail.internet.MimeMessage;
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
import com.fsck.k9.mail.internet.MimeMultipart;
|
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
import com.fsck.k9.mailstore.CryptoResultAnnotation;
|
import com.fsck.k9.mailstore.CryptoResultAnnotation;
|
||||||
import com.fsck.k9.mailstore.CryptoResultAnnotation.CryptoError;
|
import com.fsck.k9.mailstore.CryptoResultAnnotation.CryptoError;
|
||||||
|
@ -37,11 +35,13 @@ import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
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.assertEquals;
|
||||||
import static junit.framework.Assert.assertSame;
|
import static junit.framework.Assert.assertSame;
|
||||||
import static junit.framework.Assert.assertTrue;
|
import static junit.framework.Assert.assertTrue;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.eq;
|
|
||||||
import static org.mockito.Matchers.same;
|
import static org.mockito.Matchers.same;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
|
@ -84,7 +84,7 @@ public class MessageCryptoHelperTest {
|
||||||
message.setHeader("Content-Type", "text/plain");
|
message.setHeader("Content-Type", "text/plain");
|
||||||
|
|
||||||
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
||||||
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null);
|
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null, false);
|
||||||
|
|
||||||
ArgumentCaptor<MessageCryptoAnnotations> captor = ArgumentCaptor.forClass(MessageCryptoAnnotations.class);
|
ArgumentCaptor<MessageCryptoAnnotations> captor = ArgumentCaptor.forClass(MessageCryptoAnnotations.class);
|
||||||
verify(messageCryptoCallback).onCryptoOperationsFinished(captor.capture());
|
verify(messageCryptoCallback).onCryptoOperationsFinished(captor.capture());
|
||||||
|
@ -107,7 +107,7 @@ public class MessageCryptoHelperTest {
|
||||||
|
|
||||||
|
|
||||||
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
||||||
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null);
|
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null, false);
|
||||||
|
|
||||||
|
|
||||||
ArgumentCaptor<MessageCryptoAnnotations> captor = ArgumentCaptor.forClass(MessageCryptoAnnotations.class);
|
ArgumentCaptor<MessageCryptoAnnotations> captor = ArgumentCaptor.forClass(MessageCryptoAnnotations.class);
|
||||||
|
@ -124,12 +124,15 @@ public class MessageCryptoHelperTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multipartSigned__withNullBody__shouldReturnSignedIncomplete() throws Exception {
|
public void multipartSigned__withNullBody__shouldReturnSignedIncomplete() throws Exception {
|
||||||
MimeMessage message = new MimeMessage();
|
Message message = messageFromBody(
|
||||||
message.setUid("msguid");
|
multipart("signed", "protocol=\"application/pgp-signature\"",
|
||||||
message.setHeader("Content-Type", "multipart/signed");
|
bodypart("text/plain"),
|
||||||
|
bodypart("application/pgp-signature")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
||||||
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null);
|
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null, true);
|
||||||
|
|
||||||
assertPartAnnotationHasState(message, messageCryptoCallback, CryptoError.OPENPGP_SIGNED_BUT_INCOMPLETE, null,
|
assertPartAnnotationHasState(message, messageCryptoCallback, CryptoError.OPENPGP_SIGNED_BUT_INCOMPLETE, null,
|
||||||
null, null, null);
|
null, null, null);
|
||||||
|
@ -137,12 +140,15 @@ public class MessageCryptoHelperTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multipartEncrypted__withNullBody__shouldReturnEncryptedIncomplete() throws Exception {
|
public void multipartEncrypted__withNullBody__shouldReturnEncryptedIncomplete() throws Exception {
|
||||||
MimeMessage message = new MimeMessage();
|
Message message = messageFromBody(
|
||||||
message.setUid("msguid");
|
multipart("encrypted", "protocol=\"application/pgp-encrypted\"",
|
||||||
message.setHeader("Content-Type", "multipart/encrypted");
|
bodypart("application/pgp-encrypted"),
|
||||||
|
bodypart("application/octet-stream")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
||||||
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null);
|
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null, false);
|
||||||
|
|
||||||
assertPartAnnotationHasState(
|
assertPartAnnotationHasState(
|
||||||
message, messageCryptoCallback, CryptoError.OPENPGP_ENCRYPTED_BUT_INCOMPLETE, null, null, null, null);
|
message, messageCryptoCallback, CryptoError.OPENPGP_ENCRYPTED_BUT_INCOMPLETE, null, null, null, null);
|
||||||
|
@ -150,13 +156,15 @@ public class MessageCryptoHelperTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multipartEncrypted__withUnknownProtocol__shouldReturnEncryptedUnsupported() throws Exception {
|
public void multipartEncrypted__withUnknownProtocol__shouldReturnEncryptedUnsupported() throws Exception {
|
||||||
MimeMessage message = new MimeMessage();
|
Message message = messageFromBody(
|
||||||
message.setUid("msguid");
|
multipart("encrypted", "protocol=\"application/bad-protocol\"",
|
||||||
message.setHeader("Content-Type", "multipart/encrypted; protocol=\"unknown protocol\"");
|
bodypart("application/bad-protocol", "content"),
|
||||||
message.setBody(new MimeMultipart("multipart/encrypted", "--------"));
|
bodypart("application/octet-stream", "content")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
||||||
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null);
|
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null, false);
|
||||||
|
|
||||||
assertPartAnnotationHasState(message, messageCryptoCallback, CryptoError.ENCRYPTED_BUT_UNSUPPORTED, null, null,
|
assertPartAnnotationHasState(message, messageCryptoCallback, CryptoError.ENCRYPTED_BUT_UNSUPPORTED, null, null,
|
||||||
null, null);
|
null, null);
|
||||||
|
@ -164,13 +172,15 @@ public class MessageCryptoHelperTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multipartSigned__withUnknownProtocol__shouldReturnSignedUnsupported() throws Exception {
|
public void multipartSigned__withUnknownProtocol__shouldReturnSignedUnsupported() throws Exception {
|
||||||
MimeMessage message = new MimeMessage();
|
Message message = messageFromBody(
|
||||||
message.setUid("msguid");
|
multipart("signed", "protocol=\"application/bad-protocol\"",
|
||||||
message.setHeader("Content-Type", "multipart/signed; protocol=\"unknown protocol\"");
|
bodypart("text/plain", "content"),
|
||||||
message.setBody(new MimeMultipart("multipart/encrypted", "--------"));
|
bodypart("application/bad-protocol", "content")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
||||||
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null);
|
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null, true);
|
||||||
|
|
||||||
assertPartAnnotationHasState(message, messageCryptoCallback, CryptoError.SIGNED_BUT_UNSUPPORTED, null, null,
|
assertPartAnnotationHasState(message, messageCryptoCallback, CryptoError.SIGNED_BUT_UNSUPPORTED, null, null,
|
||||||
null, null);
|
null, null);
|
||||||
|
@ -178,18 +188,14 @@ public class MessageCryptoHelperTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multipartSigned__shouldCallOpenPgpApiAsync() throws Exception {
|
public void multipartSigned__shouldCallOpenPgpApiAsync() throws Exception {
|
||||||
BodyPart signedBodyPart = spy(new MimeBodyPart(new TextBody("text")));
|
BodyPart signedBodyPart = spy(bodypart("text/plain", "content"));
|
||||||
BodyPart signatureBodyPart = new MimeBodyPart(new TextBody("text"));
|
Message message = messageFromBody(
|
||||||
|
multipart("signed", "protocol=\"application/pgp-signature\"",
|
||||||
Multipart messageBody = new MimeMultipart("boundary1");
|
signedBodyPart,
|
||||||
messageBody.addBodyPart(signedBodyPart);
|
bodypart("application/pgp-signature", "content")
|
||||||
messageBody.addBodyPart(signatureBodyPart);
|
)
|
||||||
|
);
|
||||||
MimeMessage message = new MimeMessage();
|
|
||||||
message.setUid("msguid");
|
|
||||||
message.setHeader("Content-Type", "multipart/signed; protocol=\"application/pgp-signature\"");
|
|
||||||
message.setFrom(Address.parse("Test <test@example.org>")[0]);
|
message.setFrom(Address.parse("Test <test@example.org>")[0]);
|
||||||
message.setBody(messageBody);
|
|
||||||
|
|
||||||
OutputStream outputStream = mock(OutputStream.class);
|
OutputStream outputStream = mock(OutputStream.class);
|
||||||
|
|
||||||
|
@ -204,21 +210,46 @@ public class MessageCryptoHelperTest {
|
||||||
verifyNoMoreInteractions(autocryptOperations);
|
verifyNoMoreInteractions(autocryptOperations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipartSigned__withSignOnlyDisabled__shouldReturnNothing() throws Exception {
|
||||||
|
Message message = messageFromBody(
|
||||||
|
multipart("signed", "protocol=\"application/pgp-signature\"",
|
||||||
|
bodypart("text/plain", "content"),
|
||||||
|
bodypart("application/pgp-signature", "content")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
||||||
|
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null, false);
|
||||||
|
|
||||||
|
assertReturnsWithNoCryptoAnnotations(messageCryptoCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipartSigned__withSignOnlyDisabledAndNullBody__shouldReturnNothing() throws Exception {
|
||||||
|
Message message = messageFromBody(
|
||||||
|
multipart("signed", "protocol=\"application/pgp-signature\"",
|
||||||
|
bodypart("text/plain"),
|
||||||
|
bodypart("application/pgp-signature")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
|
||||||
|
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null, false);
|
||||||
|
|
||||||
|
assertReturnsWithNoCryptoAnnotations(messageCryptoCallback);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multipartEncrypted__shouldCallOpenPgpApiAsync() throws Exception {
|
public void multipartEncrypted__shouldCallOpenPgpApiAsync() throws Exception {
|
||||||
BodyPart dummyBodyPart = new MimeBodyPart(new TextBody("text"));
|
|
||||||
Body encryptedBody = spy(new TextBody("encrypted data"));
|
Body encryptedBody = spy(new TextBody("encrypted data"));
|
||||||
BodyPart encryptedBodyPart = spy(new MimeBodyPart(encryptedBody));
|
Message message = messageFromBody(
|
||||||
|
multipart("encrypted", "protocol=\"application/pgp-encrypted\"",
|
||||||
Multipart messageBody = new MimeMultipart("boundary1");
|
bodypart("application/pgp-encrypted", "content"),
|
||||||
messageBody.addBodyPart(dummyBodyPart);
|
bodypart("application/octet-stream", encryptedBody)
|
||||||
messageBody.addBodyPart(encryptedBodyPart);
|
)
|
||||||
|
);
|
||||||
MimeMessage message = new MimeMessage();
|
|
||||||
message.setUid("msguid");
|
|
||||||
message.setHeader("Content-Type", "multipart/encrypted; protocol=\"application/pgp-encrypted\"");
|
|
||||||
message.setFrom(Address.parse("Test <test@example.org>")[0]);
|
message.setFrom(Address.parse("Test <test@example.org>")[0]);
|
||||||
message.setBody(messageBody);
|
|
||||||
|
|
||||||
OutputStream outputStream = mock(OutputStream.class);
|
OutputStream outputStream = mock(OutputStream.class);
|
||||||
|
|
||||||
|
@ -248,7 +279,7 @@ public class MessageCryptoHelperTest {
|
||||||
|
|
||||||
private void processEncryptedMessageAndCaptureMocks(Message message, Body encryptedBody, OutputStream outputStream)
|
private void processEncryptedMessageAndCaptureMocks(Message message, Body encryptedBody, OutputStream outputStream)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null);
|
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null, false);
|
||||||
|
|
||||||
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||||
ArgumentCaptor<OpenPgpDataSource> dataSourceCaptor = ArgumentCaptor.forClass(OpenPgpDataSource.class);
|
ArgumentCaptor<OpenPgpDataSource> dataSourceCaptor = ArgumentCaptor.forClass(OpenPgpDataSource.class);
|
||||||
|
@ -266,7 +297,7 @@ public class MessageCryptoHelperTest {
|
||||||
|
|
||||||
private void processSignedMessageAndCaptureMocks(Message message, BodyPart signedBodyPart,
|
private void processSignedMessageAndCaptureMocks(Message message, BodyPart signedBodyPart,
|
||||||
OutputStream outputStream) throws Exception {
|
OutputStream outputStream) throws Exception {
|
||||||
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null);
|
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null, true);
|
||||||
|
|
||||||
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||||
ArgumentCaptor<OpenPgpDataSource> dataSourceCaptor = ArgumentCaptor.forClass(OpenPgpDataSource.class);
|
ArgumentCaptor<OpenPgpDataSource> dataSourceCaptor = ArgumentCaptor.forClass(OpenPgpDataSource.class);
|
||||||
|
@ -282,6 +313,15 @@ public class MessageCryptoHelperTest {
|
||||||
verify(signedBodyPart).writeTo(outputStream);
|
verify(signedBodyPart).writeTo(outputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertReturnsWithNoCryptoAnnotations(MessageCryptoCallback messageCryptoCallback) {
|
||||||
|
ArgumentCaptor<MessageCryptoAnnotations> captor = ArgumentCaptor.forClass(MessageCryptoAnnotations.class);
|
||||||
|
verify(messageCryptoCallback).onCryptoOperationsFinished(captor.capture());
|
||||||
|
verifyNoMoreInteractions(messageCryptoCallback);
|
||||||
|
|
||||||
|
MessageCryptoAnnotations annotations = captor.getValue();
|
||||||
|
assertTrue(annotations.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
private void assertPartAnnotationHasState(Message message, MessageCryptoCallback messageCryptoCallback,
|
private void assertPartAnnotationHasState(Message message, MessageCryptoCallback messageCryptoCallback,
|
||||||
CryptoError cryptoErrorState, MimeBodyPart replacementPart, OpenPgpDecryptionResult openPgpDecryptionResult,
|
CryptoError cryptoErrorState, MimeBodyPart replacementPart, OpenPgpDecryptionResult openPgpDecryptionResult,
|
||||||
OpenPgpSignatureResult openPgpSignatureResult, PendingIntent openPgpPendingIntent) {
|
OpenPgpSignatureResult openPgpSignatureResult, PendingIntent openPgpPendingIntent) {
|
||||||
|
|
|
@ -80,7 +80,7 @@ public class OpenPgpUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String extractClearsignedMessage(String text) {
|
public static String extractClearsignedMessage(String text) {
|
||||||
if (!text.startsWith(PGP_MARKER_CLEARSIGN_BEGIN_MESSAGE)) {
|
if (text == null || !text.startsWith(PGP_MARKER_CLEARSIGN_BEGIN_MESSAGE)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int endOfHeader = text.indexOf("\r\n\r\n") +4;
|
int endOfHeader = text.indexOf("\r\n\r\n") +4;
|
||||||
|
|
Loading…
Reference in a new issue