nicer MessageCryptoHelper structure, and handle plain autocrypt asynchronously

This commit is contained in:
Vincent Breitmoser 2017-08-21 20:30:32 +02:00
parent a7aaefe404
commit 2e7c6cf5e8
2 changed files with 75 additions and 72 deletions

View file

@ -1,7 +1,6 @@
package com.fsck.k9.crypto;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
@ -12,7 +11,6 @@ import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.internet.MimeMessage;
import com.fsck.k9.mail.internet.MimeUtility;
import okio.ByteString;
import org.openintents.openpgp.AutocryptPeerUpdate;
@ -59,14 +57,6 @@ public class AutocryptOperations {
return true;
}
public void processCleartextMessage(OpenPgpApi openPgpApi, Message currentMessage) {
Intent intent = new Intent(OpenPgpApi.ACTION_UPDATE_AUTOCRYPT_PEER);
boolean hasInlineKeyData = addAutocryptPeerUpdateToIntentIfPresent(currentMessage, intent);
if (hasInlineKeyData) {
openPgpApi.executeApi(intent, (InputStream) null, null);
}
}
@Nullable
@VisibleForTesting
AutocryptHeader getValidAutocryptHeader(Message currentMessage) {

View file

@ -17,10 +17,8 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import com.fsck.k9.crypto.AutocryptOperations;
import timber.log.Timber;
import com.fsck.k9.K9;
import com.fsck.k9.crypto.AutocryptOperations;
import com.fsck.k9.crypto.MessageDecryptVerifier;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Body;
@ -48,6 +46,7 @@ import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpApi.CancelableBackgroundOperation;
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpSinkResultCallback;
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSink;
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource;
@ -67,7 +66,7 @@ public class MessageCryptoHelper {
private final String openPgpProviderPackage;
private final AutocryptOperations autocryptOperations;
private final Object callbackLock = new Object();
private final Deque<CryptoPart> partsToDecryptOrVerify = new ArrayDeque<>();
private final Deque<CryptoPart> partsToProcess = new ArrayDeque<>();
@Nullable
private MessageCryptoCallback callback;
@ -82,6 +81,7 @@ public class MessageCryptoHelper {
private CryptoPart currentCryptoPart;
private Intent currentCryptoResult;
private Intent userInteractionResultIntent;
private boolean firstPassStarted;
private boolean secondPassStarted;
private boolean thirdPassStarted;
private CancelableBackgroundOperation cancelableBackgroundOperation;
@ -120,17 +120,17 @@ public class MessageCryptoHelper {
this.cachedDecryptionResult = cachedDecryptionResult;
this.callback = callback;
runFirstPass();
nextStep();
}
private void runFirstPass() {
private void findPartsForFirstPass() {
firstPassStarted = true;
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(currentMessage);
processFoundEncryptedParts(encryptedParts);
decryptOrVerifyNextPart();
}
private void runSecondPass() {
private void findPartsForSecondPass() {
secondPassStarted = true;
List<Part> signedParts = MessageDecryptVerifier.findSignedParts(currentMessage, messageAnnotations);
@ -138,23 +138,18 @@ public class MessageCryptoHelper {
List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(currentMessage);
addFoundInlinePgpParts(inlineParts);
decryptOrVerifyNextPart();
}
private void runThirdPass() {
private void findPartsForThirdPass() {
thirdPassStarted = true;
if (messageAnnotations.isEmpty()) {
if (autocryptOperations.hasAutocryptHeader(currentMessage)) {
boolean noOtherCryptoPerformed = messageAnnotations.isEmpty();
if (noOtherCryptoPerformed && autocryptOperations.hasAutocryptHeader(currentMessage)) {
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PLAIN_AUTOCRYPT, currentMessage);
partsToDecryptOrVerify.add(cryptoPart);
partsToProcess.add(cryptoPart);
}
}
decryptOrVerifyNextPart();
}
private void processFoundEncryptedParts(List<Part> foundParts) {
for (Part part : foundParts) {
if (!MessageHelper.isCompletePartAvailable(part)) {
@ -163,7 +158,7 @@ public class MessageCryptoHelper {
}
if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) {
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_ENCRYPTED, part);
partsToDecryptOrVerify.add(cryptoPart);
partsToProcess.add(cryptoPart);
continue;
}
addErrorAnnotation(part, CryptoError.ENCRYPTED_BUT_UNSUPPORTED, MessageHelper.createEmptyPart());
@ -179,7 +174,7 @@ public class MessageCryptoHelper {
}
if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) {
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_SIGNED, part);
partsToDecryptOrVerify.add(cryptoPart);
partsToProcess.add(cryptoPart);
continue;
}
MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part);
@ -205,29 +200,34 @@ public class MessageCryptoHelper {
}
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_INLINE, part);
partsToDecryptOrVerify.add(cryptoPart);
partsToProcess.add(cryptoPart);
}
}
private void decryptOrVerifyNextPart() {
private void nextStep() {
if (isCancelled) {
return;
}
if (partsToDecryptOrVerify.isEmpty()) {
runNextPassOrReturnResultToFragment();
while (partsToProcess.isEmpty()) {
boolean hadNextPass = findPartsForNextPass();
if (!hadNextPass) {
callbackReturnResult();
return;
}
}
if (!isBoundToCryptoProviderService()) {
connectToCryptoProviderService();
return;
}
CryptoPart cryptoPart = partsToDecryptOrVerify.peekFirst();
startDecryptingOrVerifyingPart(cryptoPart);
}
private void startDecryptingOrVerifyingPart(CryptoPart cryptoPart) {
if (!isBoundToCryptoProviderService()) {
connectToCryptoProviderService();
currentCryptoPart = partsToProcess.peekFirst();
if (currentCryptoPart.type == CryptoPartType.PLAIN_AUTOCRYPT) {
processAutocryptHeaderForCurrentPart();
} else {
decryptOrVerifyPart(cryptoPart);
decryptOrVerifyCurrentPart();
}
}
@ -243,7 +243,7 @@ public class MessageCryptoHelper {
public void onBound(IOpenPgpService2 service) {
openPgpApi = openPgpApiFactory.createOpenPgpApi(context, service);
decryptOrVerifyNextPart();
nextStep();
}
@Override
@ -255,14 +255,13 @@ public class MessageCryptoHelper {
openPgpServiceConnection.bindToService();
}
private void decryptOrVerifyPart(CryptoPart cryptoPart) {
currentCryptoPart = cryptoPart;
Intent decryptIntent = userInteractionResultIntent;
private void decryptOrVerifyCurrentPart() {
Intent apiIntent = userInteractionResultIntent;
userInteractionResultIntent = null;
if (decryptIntent == null) {
decryptIntent = getDecryptVerifyIntent();
if (apiIntent == null) {
apiIntent = getDecryptVerifyIntent();
}
decryptVerify(decryptIntent);
decryptVerify(apiIntent);
}
@NonNull
@ -283,26 +282,24 @@ public class MessageCryptoHelper {
return decryptIntent;
}
private void decryptVerify(Intent intent) {
private void decryptVerify(Intent apiIntent) {
try {
CryptoPartType cryptoPartType = currentCryptoPart.type;
switch (cryptoPartType) {
case PGP_SIGNED: {
callAsyncDetachedVerify(intent);
callAsyncDetachedVerify(apiIntent);
return;
}
case PGP_ENCRYPTED: {
callAsyncDecrypt(intent);
callAsyncDecrypt(apiIntent);
return;
}
case PGP_INLINE: {
callAsyncInlineOperation(intent);
return;
}
case PLAIN_AUTOCRYPT: {
callAsyncParseAutocryptHeaderOperation();
callAsyncInlineOperation(apiIntent);
return;
}
case PLAIN_AUTOCRYPT:
throw new IllegalStateException("This part type must have been handled previously!");
}
throw new IllegalStateException("Unknown crypto part type: " + cryptoPartType);
@ -313,9 +310,20 @@ public class MessageCryptoHelper {
}
}
private void callAsyncParseAutocryptHeaderOperation() {
// TODO make actually async (then callback to onCryptoFinished)
autocryptOperations.processCleartextMessage(openPgpApi, (Message) currentCryptoPart.part);
private void processAutocryptHeaderForCurrentPart() {
Intent intent = new Intent(OpenPgpApi.ACTION_UPDATE_AUTOCRYPT_PEER);
boolean hasInlineKeyData = autocryptOperations.addAutocryptPeerUpdateToIntentIfPresent(
(Message) currentCryptoPart.part, intent);
if (hasInlineKeyData) {
Timber.d("Passing autocrypt data from plain mail to OpenPGP API");
// We don't care about the result here, so we just call this fire-and-forget wait to minimize delay
openPgpApi.executeApiAsync(intent, null, null, new IOpenPgpCallback() {
@Override
public void onReturn(Intent result) {
Timber.d("Autocrypt update OK!");
}
});
}
onCryptoFinished();
}
@ -568,7 +576,7 @@ public class MessageCryptoHelper {
}
if (resultCode == Activity.RESULT_OK) {
userInteractionResultIntent = data;
decryptOrVerifyNextPart();
nextStep();
} else {
onCryptoOperationCanceled();
}
@ -620,34 +628,39 @@ public class MessageCryptoHelper {
}
private void onCryptoFinished() {
boolean currentPartIsFirstInQueue = partsToDecryptOrVerify.peekFirst() == currentCryptoPart;
boolean currentPartIsFirstInQueue = partsToProcess.peekFirst() == currentCryptoPart;
if (!currentPartIsFirstInQueue) {
throw new IllegalStateException(
"Trying to remove part from queue that is not the currently processed one!");
}
if (currentCryptoPart != null) {
partsToDecryptOrVerify.removeFirst();
partsToProcess.removeFirst();
currentCryptoPart = null;
} else {
Timber.e(new Throwable(), "Got to onCryptoFinished() with no part in processing!");
}
decryptOrVerifyNextPart();
nextStep();
}
private void runNextPassOrReturnResultToFragment() {
private boolean findPartsForNextPass() {
if (!firstPassStarted) {
findPartsForFirstPass();
return true;
}
if (!secondPassStarted) {
runSecondPass();
return;
findPartsForSecondPass();
return true;
}
if (!thirdPassStarted) {
runThirdPass();
return;
findPartsForThirdPass();
return true;
}
callbackReturnResult();
return false;
}
private void cleanupAfterProcessingFinished() {
partsToDecryptOrVerify.clear();
partsToProcess.clear();
openPgpApi = null;
if (openPgpServiceConnection != null) {
openPgpServiceConnection.unbindFromService();