mirror of
https://github.com/moparisthebest/k-9
synced 2025-01-14 07:08:00 -05:00
extract crypto methods from MessageViewFragment into MessageCryptoHelper
This commit is contained in:
parent
00b7b74878
commit
d678ccc160
@ -0,0 +1,357 @@
|
||||
package com.fsck.k9.ui.messageview;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender.SendIntentException;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.Identity;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.activity.MessageList;
|
||||
import com.fsck.k9.crypto.MessageDecryptVerifyer;
|
||||
import com.fsck.k9.crypto.OpenPgpApiHelper;
|
||||
import com.fsck.k9.helper.IdentityHelper;
|
||||
import com.fsck.k9.mail.Body;
|
||||
import com.fsck.k9.mail.BodyPart;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Multipart;
|
||||
import com.fsck.k9.mail.Part;
|
||||
import com.fsck.k9.mailstore.DecryptStreamParser;
|
||||
import com.fsck.k9.mailstore.LocalMessage;
|
||||
import com.fsck.k9.mailstore.OpenPgpResultBodyPart;
|
||||
import org.openintents.openpgp.IOpenPgpService;
|
||||
import org.openintents.openpgp.OpenPgpError;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.openintents.openpgp.util.OpenPgpApi;
|
||||
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
|
||||
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
||||
import org.openintents.openpgp.util.OpenPgpServiceConnection.OnBound;
|
||||
|
||||
|
||||
public class MessageCryptoHelper {
|
||||
|
||||
private MessageViewFragment fragment;
|
||||
private Account account;
|
||||
private LocalMessage message;
|
||||
|
||||
private Deque<Part> partsToDecryptOrVerify;
|
||||
private OpenPgpApi openPgpApi;
|
||||
private Part currentlyDecrypringOrVerifyingPart;
|
||||
private Intent currentCryptoResult;
|
||||
|
||||
private static final int INVALID_OPENPGP_RESULT_CODE = -1;
|
||||
|
||||
MessageCryptoHelper(MessageViewFragment fragment, Account account) {
|
||||
this.fragment = fragment;
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
void decryptOrVerifyMessagePartsIfNecessary(LocalMessage message) {
|
||||
this.message = message;
|
||||
|
||||
List<Part> encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message);
|
||||
List<Part> signedParts = MessageDecryptVerifyer.findSignedParts(message);
|
||||
if (!encryptedParts.isEmpty() || !signedParts.isEmpty()) {
|
||||
partsToDecryptOrVerify = new ArrayDeque<Part>();
|
||||
partsToDecryptOrVerify.addAll(encryptedParts);
|
||||
partsToDecryptOrVerify.addAll(signedParts);
|
||||
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||
} else {
|
||||
returnResultToFragment();
|
||||
}
|
||||
}
|
||||
|
||||
private void decryptOrVerifyNextPartOrStartExtractingTextAndAttachments() {
|
||||
if (!partsToDecryptOrVerify.isEmpty()) {
|
||||
|
||||
Part part = partsToDecryptOrVerify.peekFirst();
|
||||
if (MessageDecryptVerifyer.isPgpMimePart(part)) {
|
||||
startDecryptingOrVerifyingPart(part);
|
||||
} else {
|
||||
partsToDecryptOrVerify.removeFirst();
|
||||
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
returnResultToFragment();
|
||||
|
||||
}
|
||||
|
||||
private void startDecryptingOrVerifyingPart(Part part) {
|
||||
Multipart multipart = (Multipart) part.getBody();
|
||||
if (multipart == null) {
|
||||
throw new RuntimeException("Downloading missing parts before decryption isn't supported yet");
|
||||
}
|
||||
|
||||
if (!isBoundToCryptoProviderService()) {
|
||||
connectToCryptoProviderService();
|
||||
} else {
|
||||
decryptOrVerifyPart(part);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBoundToCryptoProviderService() {
|
||||
return openPgpApi != null;
|
||||
}
|
||||
|
||||
private void connectToCryptoProviderService() {
|
||||
String openPgpProvider = account.getOpenPgpProvider();
|
||||
new OpenPgpServiceConnection(fragment.getContext(), openPgpProvider,
|
||||
new OnBound() {
|
||||
@Override
|
||||
public void onBound(IOpenPgpService service) {
|
||||
openPgpApi = new OpenPgpApi(fragment.getContext(), service);
|
||||
|
||||
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Couldn't connect to OpenPgpService", e);
|
||||
}
|
||||
}).bindToService();
|
||||
}
|
||||
|
||||
private void decryptOrVerifyPart(Part part) {
|
||||
currentlyDecrypringOrVerifyingPart = part;
|
||||
decryptVerify(new Intent());
|
||||
}
|
||||
|
||||
private void decryptVerify(Intent intent) {
|
||||
intent.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
|
||||
|
||||
Identity identity = IdentityHelper.getRecipientIdentityFromMessage(account, message);
|
||||
String accountName = OpenPgpApiHelper.buildAccountName(identity);
|
||||
intent.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, accountName);
|
||||
|
||||
try {
|
||||
if (MessageDecryptVerifyer.isPgpMimeSignedPart(currentlyDecrypringOrVerifyingPart)) {
|
||||
callAsyncDetachedVerify(intent);
|
||||
} else {
|
||||
callAsyncDecrypt(intent);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(K9.LOG_TAG, "IOException", e);
|
||||
} catch (MessagingException e) {
|
||||
Log.e(K9.LOG_TAG, "MessagingException", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void callAsyncDecrypt(Intent intent) throws IOException {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
PipedInputStream pipedInputStream = getPipedInputStreamForEncryptedData();
|
||||
PipedOutputStream decryptedOutputStream = getPipedOutputStreamForDecryptedData(latch);
|
||||
|
||||
openPgpApi.executeApiAsync(intent, pipedInputStream, decryptedOutputStream, new IOpenPgpCallback() {
|
||||
@Override
|
||||
public void onReturn(Intent result) {
|
||||
currentCryptoResult = result;
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void callAsyncDetachedVerify(Intent intent) throws IOException, MessagingException {
|
||||
PipedInputStream pipedInputStream = getPipedInputStreamForSignedData();
|
||||
|
||||
byte[] signatureData = MessageDecryptVerifyer.getSignatureData(currentlyDecrypringOrVerifyingPart);
|
||||
intent.putExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE, signatureData);
|
||||
|
||||
openPgpApi.executeApiAsync(intent, pipedInputStream, null, new IOpenPgpCallback() {
|
||||
@Override
|
||||
public void onReturn(Intent result) {
|
||||
currentCryptoResult = result;
|
||||
onCryptoConverge(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private PipedInputStream getPipedInputStreamForSignedData() throws IOException {
|
||||
PipedInputStream pipedInputStream = new PipedInputStream();
|
||||
|
||||
final PipedOutputStream out = new PipedOutputStream(pipedInputStream);
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Multipart multipartSignedMultipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody();
|
||||
BodyPart signatureBodyPart = multipartSignedMultipart.getBodyPart(0);
|
||||
Log.d(K9.LOG_TAG, "signed data type: " + signatureBodyPart.getMimeType());
|
||||
signatureBodyPart.writeTo(out);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Exception while writing message to crypto provider", e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
return pipedInputStream;
|
||||
}
|
||||
|
||||
private PipedInputStream getPipedInputStreamForEncryptedData() throws IOException {
|
||||
PipedInputStream pipedInputStream = new PipedInputStream();
|
||||
|
||||
final PipedOutputStream out = new PipedOutputStream(pipedInputStream);
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Multipart multipartEncryptedMultipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody();
|
||||
BodyPart encryptionPayloadPart = multipartEncryptedMultipart.getBodyPart(1);
|
||||
Body encryptionPayloadBody = encryptionPayloadPart.getBody();
|
||||
encryptionPayloadBody.writeTo(out);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Exception while writing message to crypto provider", e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
return pipedInputStream;
|
||||
}
|
||||
|
||||
private PipedOutputStream getPipedOutputStreamForDecryptedData(final CountDownLatch latch) throws IOException {
|
||||
PipedOutputStream decryptedOutputStream = new PipedOutputStream();
|
||||
final PipedInputStream decryptedInputStream = new PipedInputStream(decryptedOutputStream);
|
||||
new AsyncTask<Void, Void, OpenPgpResultBodyPart>() {
|
||||
@Override
|
||||
protected OpenPgpResultBodyPart doInBackground(Void... params) {
|
||||
OpenPgpResultBodyPart decryptedPart = null;
|
||||
try {
|
||||
decryptedPart = DecryptStreamParser.parse(decryptedInputStream);
|
||||
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(K9.LOG_TAG, "we were interrupted while waiting for onReturn!", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Something went wrong while parsing the decrypted MIME part", e);
|
||||
//TODO: pass error to main thread and display error message to user
|
||||
}
|
||||
return decryptedPart;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(OpenPgpResultBodyPart decryptedPart) {
|
||||
onCryptoConverge(decryptedPart);
|
||||
}
|
||||
}.execute();
|
||||
return decryptedOutputStream;
|
||||
}
|
||||
|
||||
private void onCryptoConverge(OpenPgpResultBodyPart openPgpResultBodyPart) {
|
||||
try {
|
||||
if (currentCryptoResult == null) {
|
||||
Log.e(K9.LOG_TAG, "Internal error: we should have a result here!");
|
||||
return;
|
||||
}
|
||||
|
||||
int resultCode = currentCryptoResult.getIntExtra(OpenPgpApi.RESULT_CODE, INVALID_OPENPGP_RESULT_CODE);
|
||||
if (K9.DEBUG) {
|
||||
Log.d(K9.LOG_TAG, "OpenPGP API decryptVerify result code: " + resultCode);
|
||||
}
|
||||
|
||||
switch (resultCode) {
|
||||
case INVALID_OPENPGP_RESULT_CODE: {
|
||||
Log.e(K9.LOG_TAG, "Internal error: no result code!");
|
||||
break;
|
||||
}
|
||||
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
|
||||
PendingIntent pendingIntent = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
|
||||
if (pendingIntent == null) {
|
||||
throw new AssertionError("Expecting PendingIntent on USER_INTERACTION_REQUIRED!");
|
||||
}
|
||||
|
||||
try {
|
||||
fragment.getActivity().startIntentSenderForResult(pendingIntent.getIntentSender(),
|
||||
MessageList.REQUEST_CODE_CRYPTO, null, 0, 0, 0);
|
||||
} catch (SendIntentException e) {
|
||||
Log.e(K9.LOG_TAG, "Internal error on starting pendingintent!", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpenPgpApi.RESULT_CODE_ERROR: {
|
||||
OpenPgpError error = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
|
||||
|
||||
if (K9.DEBUG) {
|
||||
Log.w(K9.LOG_TAG, "OpenPGP API error: " + error.getMessage());
|
||||
}
|
||||
|
||||
onCryptoFailed(error);
|
||||
break;
|
||||
}
|
||||
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
||||
if (openPgpResultBodyPart == null) {
|
||||
openPgpResultBodyPart = new OpenPgpResultBodyPart(false);
|
||||
}
|
||||
OpenPgpSignatureResult signatureResult =
|
||||
currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
|
||||
openPgpResultBodyPart.setSignatureResult(signatureResult);
|
||||
|
||||
PendingIntent pendingIntent =
|
||||
currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
|
||||
openPgpResultBodyPart.setPendingIntent(pendingIntent);
|
||||
|
||||
onCryptoSuccess(openPgpResultBodyPart);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
// catching the empty OpenPgpResultBodyPart constructor above - this can't actually happen
|
||||
Log.e(K9.LOG_TAG, "This shouldn't happen", e);
|
||||
} finally {
|
||||
currentCryptoResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleCryptoResult(int resultCode, Intent data) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||
} else {
|
||||
// FIXME: don't pass null
|
||||
onCryptoFailed(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void onCryptoSuccess(OpenPgpResultBodyPart decryptedPart) {
|
||||
addOpenPgpResultPartToMessage(decryptedPart);
|
||||
onCryptoFinished();
|
||||
}
|
||||
|
||||
private void addOpenPgpResultPartToMessage(OpenPgpResultBodyPart decryptedPart) {
|
||||
Multipart multipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody();
|
||||
multipart.addBodyPart(decryptedPart);
|
||||
}
|
||||
|
||||
private void onCryptoFailed(OpenPgpError error) {
|
||||
try {
|
||||
OpenPgpResultBodyPart errorPart = new OpenPgpResultBodyPart(false);
|
||||
errorPart.setError(error);
|
||||
addOpenPgpResultPartToMessage(errorPart);
|
||||
} catch (MessagingException e) {
|
||||
Log.e(K9.LOG_TAG, "This shouldn't happen", e);
|
||||
}
|
||||
onCryptoFinished();
|
||||
}
|
||||
|
||||
private void onCryptoFinished() {
|
||||
partsToDecryptOrVerify.removeFirst();
|
||||
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||
}
|
||||
|
||||
private void returnResultToFragment() {
|
||||
fragment.startExtractingTextAndAttachments(message);
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +1,7 @@
|
||||
package com.fsck.k9.ui.messageview;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.DialogFragment;
|
||||
@ -21,10 +13,8 @@ import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.IntentSender.SendIntentException;
|
||||
import android.content.Loader;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
@ -38,46 +28,27 @@ import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.Identity;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.activity.ChooseFolder;
|
||||
import com.fsck.k9.activity.MessageList;
|
||||
import com.fsck.k9.activity.MessageReference;
|
||||
import com.fsck.k9.controller.MessagingController;
|
||||
import com.fsck.k9.controller.MessagingListener;
|
||||
import com.fsck.k9.crypto.MessageDecryptVerifyer;
|
||||
import com.fsck.k9.crypto.OpenPgpApiHelper;
|
||||
import com.fsck.k9.crypto.PgpData;
|
||||
import com.fsck.k9.fragment.ConfirmationDialogFragment;
|
||||
import com.fsck.k9.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
|
||||
import com.fsck.k9.fragment.ProgressDialogFragment;
|
||||
import com.fsck.k9.helper.FileBrowserHelper;
|
||||
import com.fsck.k9.helper.FileBrowserHelper.FileBrowserFailOverCallback;
|
||||
import com.fsck.k9.helper.IdentityHelper;
|
||||
import com.fsck.k9.mail.Body;
|
||||
import com.fsck.k9.mail.BodyPart;
|
||||
import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.Multipart;
|
||||
import com.fsck.k9.mail.Part;
|
||||
import com.fsck.k9.mailstore.AttachmentViewInfo;
|
||||
import com.fsck.k9.mailstore.DecryptStreamParser;
|
||||
import com.fsck.k9.mailstore.OpenPgpResultBodyPart;
|
||||
import com.fsck.k9.mailstore.LocalMessage;
|
||||
import com.fsck.k9.mailstore.MessageViewInfo;
|
||||
import com.fsck.k9.ui.message.DecodeMessageLoader;
|
||||
import com.fsck.k9.ui.message.LocalMessageLoader;
|
||||
import com.fsck.k9.view.MessageHeader;
|
||||
import org.openintents.openpgp.IOpenPgpService;
|
||||
import org.openintents.openpgp.OpenPgpError;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.openintents.openpgp.util.OpenPgpApi;
|
||||
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
|
||||
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
||||
import org.openintents.openpgp.util.OpenPgpServiceConnection.OnBound;
|
||||
|
||||
|
||||
public class MessageViewFragment extends Fragment implements ConfirmationDialogFragmentListener,
|
||||
AttachmentViewCallback, OpenPgpHeaderViewCallback {
|
||||
@ -94,9 +65,6 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
||||
private static final int LOCAL_MESSAGE_LOADER_ID = 1;
|
||||
private static final int DECODE_MESSAGE_LOADER_ID = 2;
|
||||
|
||||
private static final int INVALID_OPENPGP_RESULT_CODE = -1;
|
||||
|
||||
|
||||
public static MessageViewFragment newInstance(MessageReference reference) {
|
||||
MessageViewFragment fragment = new MessageViewFragment();
|
||||
|
||||
@ -107,7 +75,6 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
||||
return fragment;
|
||||
}
|
||||
|
||||
|
||||
private MessageTopView mMessageView;
|
||||
private PgpData mPgpData;
|
||||
private Account mAccount;
|
||||
@ -117,6 +84,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
||||
private LayoutInflater mLayoutInflater;
|
||||
private Handler handler = new Handler();
|
||||
private DownloadMessageListener downloadMessageListener = new DownloadMessageListener();
|
||||
private MessageCryptoHelper messageCryptoHelper;
|
||||
|
||||
/**
|
||||
* Used to temporarily store the destination folder for refile operations if a confirmation
|
||||
@ -139,11 +107,6 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
||||
private LoaderCallbacks<MessageViewInfo> decodeMessageLoaderCallback = new DecodeMessageLoaderCallback();
|
||||
private MessageViewInfo messageViewInfo;
|
||||
private AttachmentViewInfo currentAttachmentViewInfo;
|
||||
private Deque<Part> partsToDecryptOrVerify;
|
||||
private OpenPgpApi openPgpApi;
|
||||
private Part currentlyDecrypringOrVerifyingPart;
|
||||
private Intent currentCryptoResult;
|
||||
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
@ -232,6 +195,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
||||
|
||||
Context appContext = getActivity().getApplicationContext();
|
||||
mAccount = Preferences.getPreferences(appContext).getAccount(mMessageReference.accountUuid);
|
||||
messageCryptoHelper = new MessageCryptoHelper(this, mAccount);
|
||||
|
||||
if (resetPgpData) {
|
||||
// start with fresh, empty PGP data
|
||||
@ -247,6 +211,10 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
||||
mFragmentListener.updateMenu();
|
||||
}
|
||||
|
||||
public void handleCryptoResult(int resultCode, Intent data) {
|
||||
messageCryptoHelper.handleCryptoResult(resultCode, data);
|
||||
}
|
||||
|
||||
private void startLoadingMessageFromDatabase() {
|
||||
getLoaderManager().initLoader(LOCAL_MESSAGE_LOADER_ID, null, localMessageLoaderCallback);
|
||||
}
|
||||
@ -257,300 +225,10 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
||||
if (message.isBodyMissing()) {
|
||||
startDownloadingMessageBody(message);
|
||||
} else {
|
||||
decryptOrVerifyMessagePartsIfNecessary(message);
|
||||
messageCryptoHelper.decryptOrVerifyMessagePartsIfNecessary(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void decryptOrVerifyMessagePartsIfNecessary(LocalMessage message) {
|
||||
List<Part> encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message);
|
||||
List<Part> signedParts = MessageDecryptVerifyer.findSignedParts(message);
|
||||
if (!encryptedParts.isEmpty() || !signedParts.isEmpty()) {
|
||||
partsToDecryptOrVerify = new ArrayDeque<Part>();
|
||||
partsToDecryptOrVerify.addAll(encryptedParts);
|
||||
partsToDecryptOrVerify.addAll(signedParts);
|
||||
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||
} else {
|
||||
startExtractingTextAndAttachments(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void decryptOrVerifyNextPartOrStartExtractingTextAndAttachments() {
|
||||
if (!partsToDecryptOrVerify.isEmpty()) {
|
||||
|
||||
Part part = partsToDecryptOrVerify.peekFirst();
|
||||
if (MessageDecryptVerifyer.isPgpMimePart(part)) {
|
||||
startDecryptingOrVerifyingPart(part);
|
||||
} else {
|
||||
partsToDecryptOrVerify.removeFirst();
|
||||
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
startExtractingTextAndAttachments(mMessage);
|
||||
|
||||
}
|
||||
|
||||
private void startDecryptingOrVerifyingPart(Part part) {
|
||||
Multipart multipart = (Multipart) part.getBody();
|
||||
if (multipart == null) {
|
||||
throw new RuntimeException("Downloading missing parts before decryption isn't supported yet");
|
||||
}
|
||||
|
||||
if (!isBoundToCryptoProviderService()) {
|
||||
connectToCryptoProviderService();
|
||||
} else {
|
||||
decryptOrVerifyPart(part);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBoundToCryptoProviderService() {
|
||||
return openPgpApi != null;
|
||||
}
|
||||
|
||||
private void connectToCryptoProviderService() {
|
||||
String openPgpProvider = mAccount.getOpenPgpProvider();
|
||||
new OpenPgpServiceConnection(getContext(), openPgpProvider,
|
||||
new OnBound() {
|
||||
@Override
|
||||
public void onBound(IOpenPgpService service) {
|
||||
openPgpApi = new OpenPgpApi(getContext(), service);
|
||||
|
||||
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Couldn't connect to OpenPgpService", e);
|
||||
}
|
||||
}).bindToService();
|
||||
}
|
||||
|
||||
private void decryptOrVerifyPart(Part part) {
|
||||
currentlyDecrypringOrVerifyingPart = part;
|
||||
decryptVerify(new Intent());
|
||||
}
|
||||
|
||||
private void decryptVerify(Intent intent) {
|
||||
intent.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
|
||||
|
||||
Identity identity = IdentityHelper.getRecipientIdentityFromMessage(mAccount, mMessage);
|
||||
String accountName = OpenPgpApiHelper.buildAccountName(identity);
|
||||
intent.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, accountName);
|
||||
|
||||
try {
|
||||
if (MessageDecryptVerifyer.isPgpMimeSignedPart(currentlyDecrypringOrVerifyingPart)) {
|
||||
callAsyncDetachedVerify(intent);
|
||||
} else {
|
||||
callAsyncDecrypt(intent);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(K9.LOG_TAG, "IOException", e);
|
||||
} catch (MessagingException e) {
|
||||
Log.e(K9.LOG_TAG, "MessagingException", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void callAsyncDecrypt(Intent intent) throws IOException {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
PipedInputStream pipedInputStream = getPipedInputStreamForEncryptedData();
|
||||
PipedOutputStream decryptedOutputStream = getPipedOutputStreamForDecryptedData(latch);
|
||||
|
||||
openPgpApi.executeApiAsync(intent, pipedInputStream, decryptedOutputStream, new IOpenPgpCallback() {
|
||||
@Override
|
||||
public void onReturn(Intent result) {
|
||||
currentCryptoResult = result;
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void callAsyncDetachedVerify(Intent intent) throws IOException, MessagingException {
|
||||
PipedInputStream pipedInputStream = getPipedInputStreamForSignedData();
|
||||
|
||||
byte[] signatureData = MessageDecryptVerifyer.getSignatureData(currentlyDecrypringOrVerifyingPart);
|
||||
intent.putExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE, signatureData);
|
||||
|
||||
openPgpApi.executeApiAsync(intent, pipedInputStream, null, new IOpenPgpCallback() {
|
||||
@Override
|
||||
public void onReturn(Intent result) {
|
||||
currentCryptoResult = result;
|
||||
onCryptoConverge(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private PipedInputStream getPipedInputStreamForSignedData() throws IOException {
|
||||
PipedInputStream pipedInputStream = new PipedInputStream();
|
||||
|
||||
final PipedOutputStream out = new PipedOutputStream(pipedInputStream);
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Multipart multipartSignedMultipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody();
|
||||
BodyPart signatureBodyPart = multipartSignedMultipart.getBodyPart(0);
|
||||
Log.d(K9.LOG_TAG, "signed data type: " + signatureBodyPart.getMimeType());
|
||||
signatureBodyPart.writeTo(out);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Exception while writing message to crypto provider", e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
return pipedInputStream;
|
||||
}
|
||||
|
||||
private PipedInputStream getPipedInputStreamForEncryptedData() throws IOException {
|
||||
PipedInputStream pipedInputStream = new PipedInputStream();
|
||||
|
||||
final PipedOutputStream out = new PipedOutputStream(pipedInputStream);
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Multipart multipartEncryptedMultipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody();
|
||||
BodyPart encryptionPayloadPart = multipartEncryptedMultipart.getBodyPart(1);
|
||||
Body encryptionPayloadBody = encryptionPayloadPart.getBody();
|
||||
encryptionPayloadBody.writeTo(out);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Exception while writing message to crypto provider", e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
return pipedInputStream;
|
||||
}
|
||||
|
||||
private PipedOutputStream getPipedOutputStreamForDecryptedData(final CountDownLatch latch) throws IOException {
|
||||
PipedOutputStream decryptedOutputStream = new PipedOutputStream();
|
||||
final PipedInputStream decryptedInputStream = new PipedInputStream(decryptedOutputStream);
|
||||
new AsyncTask<Void, Void, OpenPgpResultBodyPart>() {
|
||||
@Override
|
||||
protected OpenPgpResultBodyPart doInBackground(Void... params) {
|
||||
OpenPgpResultBodyPart decryptedPart = null;
|
||||
try {
|
||||
decryptedPart = DecryptStreamParser.parse(decryptedInputStream);
|
||||
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(K9.LOG_TAG, "we were interrupted while waiting for onReturn!", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Something went wrong while parsing the decrypted MIME part", e);
|
||||
//TODO: pass error to main thread and display error message to user
|
||||
}
|
||||
return decryptedPart;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(OpenPgpResultBodyPart decryptedPart) {
|
||||
onCryptoConverge(decryptedPart);
|
||||
}
|
||||
}.execute();
|
||||
return decryptedOutputStream;
|
||||
}
|
||||
|
||||
private void onCryptoConverge(OpenPgpResultBodyPart openPgpResultBodyPart) {
|
||||
try {
|
||||
if (currentCryptoResult == null) {
|
||||
Log.e(K9.LOG_TAG, "Internal error: we should have a result here!");
|
||||
return;
|
||||
}
|
||||
|
||||
int resultCode = currentCryptoResult.getIntExtra(OpenPgpApi.RESULT_CODE, INVALID_OPENPGP_RESULT_CODE);
|
||||
if (K9.DEBUG) {
|
||||
Log.d(K9.LOG_TAG, "OpenPGP API decryptVerify result code: " + resultCode);
|
||||
}
|
||||
|
||||
switch (resultCode) {
|
||||
case INVALID_OPENPGP_RESULT_CODE: {
|
||||
Log.e(K9.LOG_TAG, "Internal error: no result code!");
|
||||
break;
|
||||
}
|
||||
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
|
||||
PendingIntent pendingIntent = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
|
||||
if (pendingIntent == null) {
|
||||
throw new AssertionError("Expecting PendingIntent on USER_INTERACTION_REQUIRED!");
|
||||
}
|
||||
|
||||
try {
|
||||
getActivity().startIntentSenderForResult(pendingIntent.getIntentSender(),
|
||||
MessageList.REQUEST_CODE_CRYPTO, null, 0, 0, 0);
|
||||
} catch (SendIntentException e) {
|
||||
Log.e(K9.LOG_TAG, "Internal error on starting pendingintent!", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpenPgpApi.RESULT_CODE_ERROR: {
|
||||
OpenPgpError error = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
|
||||
|
||||
if (K9.DEBUG) {
|
||||
Log.w(K9.LOG_TAG, "OpenPGP API error: " + error.getMessage());
|
||||
}
|
||||
|
||||
onCryptoFailed(error);
|
||||
break;
|
||||
}
|
||||
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
||||
if (openPgpResultBodyPart == null) {
|
||||
openPgpResultBodyPart = new OpenPgpResultBodyPart(false);
|
||||
}
|
||||
OpenPgpSignatureResult signatureResult =
|
||||
currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
|
||||
openPgpResultBodyPart.setSignatureResult(signatureResult);
|
||||
|
||||
PendingIntent pendingIntent =
|
||||
currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
|
||||
openPgpResultBodyPart.setPendingIntent(pendingIntent);
|
||||
|
||||
onCryptoSuccess(openPgpResultBodyPart);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
// catching the empty OpenPgpResultBodyPart constructor above - this can't actually happen
|
||||
Log.e(K9.LOG_TAG, "This shouldn't happen", e);
|
||||
} finally {
|
||||
currentCryptoResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleCryptoResult(int resultCode, Intent data) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||
} else {
|
||||
// FIXME: don't pass null
|
||||
onCryptoFailed(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void onCryptoSuccess(OpenPgpResultBodyPart decryptedPart) {
|
||||
addOpenPgpResultPartToMessage(decryptedPart);
|
||||
onCryptoFinished();
|
||||
}
|
||||
|
||||
private void addOpenPgpResultPartToMessage(OpenPgpResultBodyPart decryptedPart) {
|
||||
Multipart multipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody();
|
||||
multipart.addBodyPart(decryptedPart);
|
||||
}
|
||||
|
||||
private void onCryptoFailed(OpenPgpError error) {
|
||||
try {
|
||||
OpenPgpResultBodyPart errorPart = new OpenPgpResultBodyPart(false);
|
||||
errorPart.setError(error);
|
||||
addOpenPgpResultPartToMessage(errorPart);
|
||||
} catch (MessagingException e) {
|
||||
Log.e(K9.LOG_TAG, "This shouldn't happen", e);
|
||||
}
|
||||
onCryptoFinished();
|
||||
}
|
||||
|
||||
private void onCryptoFinished() {
|
||||
partsToDecryptOrVerify.removeFirst();
|
||||
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||
}
|
||||
|
||||
private void onLoadMessageFromDatabaseFailed() {
|
||||
// mMessageView.showStatusMessage(mContext.getString(R.string.status_invalid_id_error));
|
||||
}
|
||||
@ -580,7 +258,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
||||
Toast.makeText(mContext, errorMessage, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private void startExtractingTextAndAttachments(LocalMessage message) {
|
||||
void startExtractingTextAndAttachments(LocalMessage message) {
|
||||
getLoaderManager().initLoader(DECODE_MESSAGE_LOADER_ID, null, decodeMessageLoaderCallback);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user