nicer MessageCryptoHelper structure, and handle plain autocrypt asynchronously
This commit is contained in:
parent
a7aaefe404
commit
2e7c6cf5e8
2 changed files with 75 additions and 72 deletions
|
@ -1,7 +1,6 @@
|
||||||
package com.fsck.k9.crypto;
|
package com.fsck.k9.crypto;
|
||||||
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -12,7 +11,6 @@ import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.VisibleForTesting;
|
import android.support.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.internet.MimeMessage;
|
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import okio.ByteString;
|
import okio.ByteString;
|
||||||
import org.openintents.openpgp.AutocryptPeerUpdate;
|
import org.openintents.openpgp.AutocryptPeerUpdate;
|
||||||
|
@ -59,14 +57,6 @@ public class AutocryptOperations {
|
||||||
return true;
|
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
|
@Nullable
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
AutocryptHeader getValidAutocryptHeader(Message currentMessage) {
|
AutocryptHeader getValidAutocryptHeader(Message currentMessage) {
|
||||||
|
|
|
@ -17,10 +17,8 @@ import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.WorkerThread;
|
import android.support.annotation.WorkerThread;
|
||||||
|
|
||||||
import com.fsck.k9.crypto.AutocryptOperations;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
|
import com.fsck.k9.crypto.AutocryptOperations;
|
||||||
import com.fsck.k9.crypto.MessageDecryptVerifier;
|
import com.fsck.k9.crypto.MessageDecryptVerifier;
|
||||||
import com.fsck.k9.mail.Address;
|
import com.fsck.k9.mail.Address;
|
||||||
import com.fsck.k9.mail.Body;
|
import com.fsck.k9.mail.Body;
|
||||||
|
@ -48,6 +46,7 @@ import org.openintents.openpgp.OpenPgpError;
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
import org.openintents.openpgp.util.OpenPgpApi;
|
import org.openintents.openpgp.util.OpenPgpApi;
|
||||||
import org.openintents.openpgp.util.OpenPgpApi.CancelableBackgroundOperation;
|
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.IOpenPgpSinkResultCallback;
|
||||||
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSink;
|
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSink;
|
||||||
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource;
|
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource;
|
||||||
|
@ -67,7 +66,7 @@ public class MessageCryptoHelper {
|
||||||
private final String openPgpProviderPackage;
|
private final String openPgpProviderPackage;
|
||||||
private final AutocryptOperations autocryptOperations;
|
private final AutocryptOperations autocryptOperations;
|
||||||
private final Object callbackLock = new Object();
|
private final Object callbackLock = new Object();
|
||||||
private final Deque<CryptoPart> partsToDecryptOrVerify = new ArrayDeque<>();
|
private final Deque<CryptoPart> partsToProcess = new ArrayDeque<>();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private MessageCryptoCallback callback;
|
private MessageCryptoCallback callback;
|
||||||
|
@ -82,6 +81,7 @@ public class MessageCryptoHelper {
|
||||||
private CryptoPart currentCryptoPart;
|
private CryptoPart currentCryptoPart;
|
||||||
private Intent currentCryptoResult;
|
private Intent currentCryptoResult;
|
||||||
private Intent userInteractionResultIntent;
|
private Intent userInteractionResultIntent;
|
||||||
|
private boolean firstPassStarted;
|
||||||
private boolean secondPassStarted;
|
private boolean secondPassStarted;
|
||||||
private boolean thirdPassStarted;
|
private boolean thirdPassStarted;
|
||||||
private CancelableBackgroundOperation cancelableBackgroundOperation;
|
private CancelableBackgroundOperation cancelableBackgroundOperation;
|
||||||
|
@ -120,17 +120,17 @@ public class MessageCryptoHelper {
|
||||||
this.cachedDecryptionResult = cachedDecryptionResult;
|
this.cachedDecryptionResult = cachedDecryptionResult;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
|
||||||
runFirstPass();
|
nextStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runFirstPass() {
|
private void findPartsForFirstPass() {
|
||||||
|
firstPassStarted = true;
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(currentMessage);
|
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(currentMessage);
|
||||||
processFoundEncryptedParts(encryptedParts);
|
processFoundEncryptedParts(encryptedParts);
|
||||||
|
|
||||||
decryptOrVerifyNextPart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runSecondPass() {
|
private void findPartsForSecondPass() {
|
||||||
secondPassStarted = true;
|
secondPassStarted = true;
|
||||||
|
|
||||||
List<Part> signedParts = MessageDecryptVerifier.findSignedParts(currentMessage, messageAnnotations);
|
List<Part> signedParts = MessageDecryptVerifier.findSignedParts(currentMessage, messageAnnotations);
|
||||||
|
@ -138,23 +138,18 @@ public class MessageCryptoHelper {
|
||||||
|
|
||||||
List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(currentMessage);
|
List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(currentMessage);
|
||||||
addFoundInlinePgpParts(inlineParts);
|
addFoundInlinePgpParts(inlineParts);
|
||||||
|
|
||||||
decryptOrVerifyNextPart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runThirdPass() {
|
private void findPartsForThirdPass() {
|
||||||
thirdPassStarted = true;
|
thirdPassStarted = true;
|
||||||
|
|
||||||
if (messageAnnotations.isEmpty()) {
|
boolean noOtherCryptoPerformed = messageAnnotations.isEmpty();
|
||||||
if (autocryptOperations.hasAutocryptHeader(currentMessage)) {
|
if (noOtherCryptoPerformed && autocryptOperations.hasAutocryptHeader(currentMessage)) {
|
||||||
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PLAIN_AUTOCRYPT, currentMessage);
|
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PLAIN_AUTOCRYPT, currentMessage);
|
||||||
partsToDecryptOrVerify.add(cryptoPart);
|
partsToProcess.add(cryptoPart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decryptOrVerifyNextPart();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processFoundEncryptedParts(List<Part> foundParts) {
|
private void processFoundEncryptedParts(List<Part> foundParts) {
|
||||||
for (Part part : foundParts) {
|
for (Part part : foundParts) {
|
||||||
if (!MessageHelper.isCompletePartAvailable(part)) {
|
if (!MessageHelper.isCompletePartAvailable(part)) {
|
||||||
|
@ -163,7 +158,7 @@ public class MessageCryptoHelper {
|
||||||
}
|
}
|
||||||
if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) {
|
if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) {
|
||||||
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_ENCRYPTED, part);
|
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_ENCRYPTED, part);
|
||||||
partsToDecryptOrVerify.add(cryptoPart);
|
partsToProcess.add(cryptoPart);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
addErrorAnnotation(part, CryptoError.ENCRYPTED_BUT_UNSUPPORTED, MessageHelper.createEmptyPart());
|
addErrorAnnotation(part, CryptoError.ENCRYPTED_BUT_UNSUPPORTED, MessageHelper.createEmptyPart());
|
||||||
|
@ -179,7 +174,7 @@ public class MessageCryptoHelper {
|
||||||
}
|
}
|
||||||
if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) {
|
if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) {
|
||||||
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_SIGNED, part);
|
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_SIGNED, part);
|
||||||
partsToDecryptOrVerify.add(cryptoPart);
|
partsToProcess.add(cryptoPart);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part);
|
MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part);
|
||||||
|
@ -205,29 +200,34 @@ public class MessageCryptoHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_INLINE, part);
|
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_INLINE, part);
|
||||||
partsToDecryptOrVerify.add(cryptoPart);
|
partsToProcess.add(cryptoPart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decryptOrVerifyNextPart() {
|
private void nextStep() {
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (partsToDecryptOrVerify.isEmpty()) {
|
while (partsToProcess.isEmpty()) {
|
||||||
runNextPassOrReturnResultToFragment();
|
boolean hadNextPass = findPartsForNextPass();
|
||||||
|
|
||||||
|
if (!hadNextPass) {
|
||||||
|
callbackReturnResult();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isBoundToCryptoProviderService()) {
|
||||||
|
connectToCryptoProviderService();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoPart cryptoPart = partsToDecryptOrVerify.peekFirst();
|
currentCryptoPart = partsToProcess.peekFirst();
|
||||||
startDecryptingOrVerifyingPart(cryptoPart);
|
if (currentCryptoPart.type == CryptoPartType.PLAIN_AUTOCRYPT) {
|
||||||
}
|
processAutocryptHeaderForCurrentPart();
|
||||||
|
|
||||||
private void startDecryptingOrVerifyingPart(CryptoPart cryptoPart) {
|
|
||||||
if (!isBoundToCryptoProviderService()) {
|
|
||||||
connectToCryptoProviderService();
|
|
||||||
} else {
|
} else {
|
||||||
decryptOrVerifyPart(cryptoPart);
|
decryptOrVerifyCurrentPart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ public class MessageCryptoHelper {
|
||||||
public void onBound(IOpenPgpService2 service) {
|
public void onBound(IOpenPgpService2 service) {
|
||||||
openPgpApi = openPgpApiFactory.createOpenPgpApi(context, service);
|
openPgpApi = openPgpApiFactory.createOpenPgpApi(context, service);
|
||||||
|
|
||||||
decryptOrVerifyNextPart();
|
nextStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -255,14 +255,13 @@ public class MessageCryptoHelper {
|
||||||
openPgpServiceConnection.bindToService();
|
openPgpServiceConnection.bindToService();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decryptOrVerifyPart(CryptoPart cryptoPart) {
|
private void decryptOrVerifyCurrentPart() {
|
||||||
currentCryptoPart = cryptoPart;
|
Intent apiIntent = userInteractionResultIntent;
|
||||||
Intent decryptIntent = userInteractionResultIntent;
|
|
||||||
userInteractionResultIntent = null;
|
userInteractionResultIntent = null;
|
||||||
if (decryptIntent == null) {
|
if (apiIntent == null) {
|
||||||
decryptIntent = getDecryptVerifyIntent();
|
apiIntent = getDecryptVerifyIntent();
|
||||||
}
|
}
|
||||||
decryptVerify(decryptIntent);
|
decryptVerify(apiIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -283,26 +282,24 @@ public class MessageCryptoHelper {
|
||||||
return decryptIntent;
|
return decryptIntent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decryptVerify(Intent intent) {
|
private void decryptVerify(Intent apiIntent) {
|
||||||
try {
|
try {
|
||||||
CryptoPartType cryptoPartType = currentCryptoPart.type;
|
CryptoPartType cryptoPartType = currentCryptoPart.type;
|
||||||
switch (cryptoPartType) {
|
switch (cryptoPartType) {
|
||||||
case PGP_SIGNED: {
|
case PGP_SIGNED: {
|
||||||
callAsyncDetachedVerify(intent);
|
callAsyncDetachedVerify(apiIntent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case PGP_ENCRYPTED: {
|
case PGP_ENCRYPTED: {
|
||||||
callAsyncDecrypt(intent);
|
callAsyncDecrypt(apiIntent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case PGP_INLINE: {
|
case PGP_INLINE: {
|
||||||
callAsyncInlineOperation(intent);
|
callAsyncInlineOperation(apiIntent);
|
||||||
return;
|
|
||||||
}
|
|
||||||
case PLAIN_AUTOCRYPT: {
|
|
||||||
callAsyncParseAutocryptHeaderOperation();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case PLAIN_AUTOCRYPT:
|
||||||
|
throw new IllegalStateException("This part type must have been handled previously!");
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalStateException("Unknown crypto part type: " + cryptoPartType);
|
throw new IllegalStateException("Unknown crypto part type: " + cryptoPartType);
|
||||||
|
@ -313,9 +310,20 @@ public class MessageCryptoHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void callAsyncParseAutocryptHeaderOperation() {
|
private void processAutocryptHeaderForCurrentPart() {
|
||||||
// TODO make actually async (then callback to onCryptoFinished)
|
Intent intent = new Intent(OpenPgpApi.ACTION_UPDATE_AUTOCRYPT_PEER);
|
||||||
autocryptOperations.processCleartextMessage(openPgpApi, (Message) currentCryptoPart.part);
|
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();
|
onCryptoFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,7 +576,7 @@ public class MessageCryptoHelper {
|
||||||
}
|
}
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
userInteractionResultIntent = data;
|
userInteractionResultIntent = data;
|
||||||
decryptOrVerifyNextPart();
|
nextStep();
|
||||||
} else {
|
} else {
|
||||||
onCryptoOperationCanceled();
|
onCryptoOperationCanceled();
|
||||||
}
|
}
|
||||||
|
@ -620,34 +628,39 @@ public class MessageCryptoHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCryptoFinished() {
|
private void onCryptoFinished() {
|
||||||
boolean currentPartIsFirstInQueue = partsToDecryptOrVerify.peekFirst() == currentCryptoPart;
|
boolean currentPartIsFirstInQueue = partsToProcess.peekFirst() == currentCryptoPart;
|
||||||
if (!currentPartIsFirstInQueue) {
|
if (!currentPartIsFirstInQueue) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Trying to remove part from queue that is not the currently processed one!");
|
"Trying to remove part from queue that is not the currently processed one!");
|
||||||
}
|
}
|
||||||
if (currentCryptoPart != null) {
|
if (currentCryptoPart != null) {
|
||||||
partsToDecryptOrVerify.removeFirst();
|
partsToProcess.removeFirst();
|
||||||
currentCryptoPart = null;
|
currentCryptoPart = null;
|
||||||
} else {
|
} else {
|
||||||
Timber.e(new Throwable(), "Got to onCryptoFinished() with no part in processing!");
|
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) {
|
if (!secondPassStarted) {
|
||||||
runSecondPass();
|
findPartsForSecondPass();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
if (!thirdPassStarted) {
|
if (!thirdPassStarted) {
|
||||||
runThirdPass();
|
findPartsForThirdPass();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
callbackReturnResult();
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanupAfterProcessingFinished() {
|
private void cleanupAfterProcessingFinished() {
|
||||||
partsToDecryptOrVerify.clear();
|
partsToProcess.clear();
|
||||||
openPgpApi = null;
|
openPgpApi = null;
|
||||||
if (openPgpServiceConnection != null) {
|
if (openPgpServiceConnection != null) {
|
||||||
openPgpServiceConnection.unbindFromService();
|
openPgpServiceConnection.unbindFromService();
|
||||||
|
|
Loading…
Reference in a new issue