mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-27 11:42:16 -05:00
early support for detached signatures
This commit is contained in:
parent
38d3564c57
commit
712acf4481
@ -19,13 +19,13 @@ import static junit.framework.Assert.assertSame;
|
|||||||
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class MessageDecryptorTest {
|
public class MessageDecryptVerifyerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void findEncryptedPartsShouldReturnEmptyListForEmptyMessage() throws Exception {
|
public void findEncryptedPartsShouldReturnEmptyListForEmptyMessage() throws Exception {
|
||||||
MimeMessage emptyMessage = new MimeMessage();
|
MimeMessage emptyMessage = new MimeMessage();
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptor.findEncryptedParts(emptyMessage);
|
List<Part> encryptedParts = MessageDecryptVerifyer.findEncryptedParts(emptyMessage);
|
||||||
assertEquals(0, encryptedParts.size());
|
assertEquals(0, encryptedParts.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ public class MessageDecryptorTest {
|
|||||||
MimeMessage message = new MimeMessage();
|
MimeMessage message = new MimeMessage();
|
||||||
message.setBody(new TextBody("message text"));
|
message.setBody(new TextBody("message text"));
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptor.findEncryptedParts(message);
|
List<Part> encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message);
|
||||||
assertEquals(0, encryptedParts.size());
|
assertEquals(0, encryptedParts.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ public class MessageDecryptorTest {
|
|||||||
mulitpartEncrypted.setSubType("encrypted");
|
mulitpartEncrypted.setSubType("encrypted");
|
||||||
MimeMessageHelper.setBody(message, mulitpartEncrypted);
|
MimeMessageHelper.setBody(message, mulitpartEncrypted);
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptor.findEncryptedParts(message);
|
List<Part> encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message);
|
||||||
assertEquals(1, encryptedParts.size());
|
assertEquals(1, encryptedParts.size());
|
||||||
assertSame(message, encryptedParts.get(0));
|
assertSame(message, encryptedParts.get(0));
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ public class MessageDecryptorTest {
|
|||||||
MimeBodyPart bodyPartThree = new MimeBodyPart(mulitpartEncryptedThree);
|
MimeBodyPart bodyPartThree = new MimeBodyPart(mulitpartEncryptedThree);
|
||||||
multipartMixed.addBodyPart(bodyPartThree);
|
multipartMixed.addBodyPart(bodyPartThree);
|
||||||
|
|
||||||
List<Part> encryptedParts = MessageDecryptor.findEncryptedParts(message);
|
List<Part> encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message);
|
||||||
assertEquals(2, encryptedParts.size());
|
assertEquals(2, encryptedParts.size());
|
||||||
assertSame(bodyPartOne, encryptedParts.get(0));
|
assertSame(bodyPartOne, encryptedParts.get(0));
|
||||||
assertSame(bodyPartThree, encryptedParts.get(1));
|
assertSame(bodyPartThree, encryptedParts.get(1));
|
@ -0,0 +1,106 @@
|
|||||||
|
package com.fsck.k9.crypto;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
public class MessageDecryptVerifyer {
|
||||||
|
private static final String MULTIPART_ENCRYPTED = "multipart/encrypted";
|
||||||
|
private static final String MULTIPART_SIGNED = "multipart/signed";
|
||||||
|
private static final String PROTOCOL_PARAMETER = "protocol";
|
||||||
|
private static final String APPLICATION_PGP_ENCRYPTED = "application/pgp-encrypted";
|
||||||
|
private static final String APPLICATION_PGP_SIGNATURE = "application/pgp-signature";
|
||||||
|
|
||||||
|
public static List<Part> findEncryptedParts(Part startPart) {
|
||||||
|
List<Part> encryptedParts = new ArrayList<Part>();
|
||||||
|
Stack<Part> partsToCheck = new Stack<Part>();
|
||||||
|
partsToCheck.push(startPart);
|
||||||
|
|
||||||
|
while (!partsToCheck.isEmpty()) {
|
||||||
|
Part part = partsToCheck.pop();
|
||||||
|
String mimeType = part.getMimeType();
|
||||||
|
Body body = part.getBody();
|
||||||
|
|
||||||
|
if (MULTIPART_ENCRYPTED.equals(mimeType)) {
|
||||||
|
encryptedParts.add(part);
|
||||||
|
} else if (body instanceof Multipart) {
|
||||||
|
Multipart multipart = (Multipart) body;
|
||||||
|
for (int i = multipart.getCount() - 1; i >= 0; i--) {
|
||||||
|
BodyPart bodyPart = multipart.getBodyPart(i);
|
||||||
|
partsToCheck.push(bodyPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return encryptedParts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Part> findSignedParts(Part startPart) {
|
||||||
|
List<Part> signedParts = new ArrayList<Part>();
|
||||||
|
Stack<Part> partsToCheck = new Stack<Part>();
|
||||||
|
partsToCheck.push(startPart);
|
||||||
|
|
||||||
|
while (!partsToCheck.isEmpty()) {
|
||||||
|
Part part = partsToCheck.pop();
|
||||||
|
String mimeType = part.getMimeType();
|
||||||
|
Body body = part.getBody();
|
||||||
|
|
||||||
|
if (MULTIPART_SIGNED.equals(mimeType)) {
|
||||||
|
signedParts.add(part);
|
||||||
|
} else if (body instanceof Multipart) {
|
||||||
|
Multipart multipart = (Multipart) body;
|
||||||
|
for (int i = multipart.getCount() - 1; i >= 0; i--) {
|
||||||
|
BodyPart bodyPart = multipart.getBodyPart(i);
|
||||||
|
partsToCheck.push(bodyPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return signedParts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getSignatureData(Part part) throws IOException, MessagingException {
|
||||||
|
|
||||||
|
if (MULTIPART_SIGNED.equals(part.getMimeType())) {
|
||||||
|
Body body = part.getBody();
|
||||||
|
if (body instanceof Multipart) {
|
||||||
|
Multipart multi = (Multipart) body;
|
||||||
|
BodyPart signatureBody = multi.getBodyPart(1);
|
||||||
|
if (APPLICATION_PGP_SIGNATURE.equals(signatureBody.getMimeType())) {
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
signatureBody.getBody().writeTo(bos);
|
||||||
|
return bos.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPgpMimePart(Part part) {
|
||||||
|
return isPgpMimeSignedPart(part) || isPgpMimeEncryptedPart(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPgpMimeSignedPart(Part part) {
|
||||||
|
return MULTIPART_SIGNED.equals(part.getMimeType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPgpMimeEncryptedPart(Part part) {
|
||||||
|
//FIXME: Doesn't work right now because LocalMessage.getContentType() doesn't load headers from database
|
||||||
|
// String contentType = part.getContentType();
|
||||||
|
// String protocol = MimeUtility.getHeaderParameter(contentType, PROTOCOL_PARAMETER);
|
||||||
|
// return APPLICATION_PGP_ENCRYPTED.equals(protocol);
|
||||||
|
return MULTIPART_ENCRYPTED.equals(part.getMimeType());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,51 +0,0 @@
|
|||||||
package com.fsck.k9.crypto;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
import com.fsck.k9.mail.Body;
|
|
||||||
import com.fsck.k9.mail.BodyPart;
|
|
||||||
import com.fsck.k9.mail.Multipart;
|
|
||||||
import com.fsck.k9.mail.Part;
|
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
|
||||||
|
|
||||||
|
|
||||||
public class MessageDecryptor {
|
|
||||||
private static final String MULTIPART_ENCRYPTED = "multipart/encrypted";
|
|
||||||
private static final String PROTOCOL_PARAMETER = "protocol";
|
|
||||||
private static final String APPLICATION_PGP_ENCRYPTED = "application/pgp-encrypted";
|
|
||||||
|
|
||||||
public static List<Part> findEncryptedParts(Part startPart) {
|
|
||||||
List<Part> encryptedParts = new ArrayList<Part>();
|
|
||||||
Stack<Part> partsToCheck = new Stack<Part>();
|
|
||||||
partsToCheck.push(startPart);
|
|
||||||
|
|
||||||
while (!partsToCheck.isEmpty()) {
|
|
||||||
Part part = partsToCheck.pop();
|
|
||||||
String mimeType = part.getMimeType();
|
|
||||||
Body body = part.getBody();
|
|
||||||
|
|
||||||
if (MULTIPART_ENCRYPTED.equals(mimeType)) {
|
|
||||||
encryptedParts.add(part);
|
|
||||||
} else if (body instanceof Multipart) {
|
|
||||||
Multipart multipart = (Multipart) body;
|
|
||||||
for (int i = multipart.getCount() - 1; i >= 0; i--) {
|
|
||||||
BodyPart bodyPart = multipart.getBodyPart(i);
|
|
||||||
partsToCheck.push(bodyPart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return encryptedParts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isPgpMimeEncryptedPart(Part part) {
|
|
||||||
//FIXME: Doesn't work right now because LocalMessage.getContentType() doesn't load headers from database
|
|
||||||
// String contentType = part.getContentType();
|
|
||||||
// String protocol = MimeUtility.getHeaderParameter(contentType, PROTOCOL_PARAMETER);
|
|
||||||
// return APPLICATION_PGP_ENCRYPTED.equals(protocol);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,13 +24,11 @@ import org.apache.james.mime4j.parser.MimeStreamParser;
|
|||||||
import org.apache.james.mime4j.stream.BodyDescriptor;
|
import org.apache.james.mime4j.stream.BodyDescriptor;
|
||||||
import org.apache.james.mime4j.stream.Field;
|
import org.apache.james.mime4j.stream.Field;
|
||||||
import org.apache.james.mime4j.stream.MimeConfig;
|
import org.apache.james.mime4j.stream.MimeConfig;
|
||||||
import org.openintents.openpgp.OpenPgpError;
|
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
|
||||||
|
|
||||||
|
|
||||||
public class DecryptStreamParser {
|
public class DecryptStreamParser {
|
||||||
public static DecryptedBodyPart parse(InputStream inputStream) throws MessagingException, IOException {
|
public static OpenPgpResultBodyPart parse(InputStream inputStream) throws MessagingException, IOException {
|
||||||
DecryptedBodyPart decryptedRootPart = new DecryptedBodyPart();
|
OpenPgpResultBodyPart decryptedRootPart = new OpenPgpResultBodyPart(true);
|
||||||
|
|
||||||
MimeConfig parserConfig = new MimeConfig();
|
MimeConfig parserConfig = new MimeConfig();
|
||||||
parserConfig.setMaxHeaderLen(-1);
|
parserConfig.setMaxHeaderLen(-1);
|
||||||
@ -66,10 +64,10 @@ public class DecryptStreamParser {
|
|||||||
|
|
||||||
|
|
||||||
private static class PartBuilder implements ContentHandler {
|
private static class PartBuilder implements ContentHandler {
|
||||||
private final DecryptedBodyPart decryptedRootPart;
|
private final OpenPgpResultBodyPart decryptedRootPart;
|
||||||
private final Stack<Object> stack = new Stack<Object>();
|
private final Stack<Object> stack = new Stack<Object>();
|
||||||
|
|
||||||
public PartBuilder(DecryptedBodyPart decryptedRootPart) throws MessagingException {
|
public PartBuilder(OpenPgpResultBodyPart decryptedRootPart) throws MessagingException {
|
||||||
this.decryptedRootPart = decryptedRootPart;
|
this.decryptedRootPart = decryptedRootPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,28 +180,4 @@ public class DecryptStreamParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DecryptedBodyPart extends MimeBodyPart {
|
|
||||||
private OpenPgpSignatureResult signatureResult;
|
|
||||||
private OpenPgpError error;
|
|
||||||
|
|
||||||
public DecryptedBodyPart() throws MessagingException {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
public OpenPgpSignatureResult getSignatureResult() {
|
|
||||||
return signatureResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSignatureResult(OpenPgpSignatureResult signatureResult) {
|
|
||||||
this.signatureResult = signatureResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OpenPgpError getError() {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setError(OpenPgpError error) {
|
|
||||||
this.error = error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,9 @@ package com.fsck.k9.mailstore;
|
|||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.fsck.k9.K9;
|
|
||||||
import com.fsck.k9.R;
|
import com.fsck.k9.R;
|
||||||
import com.fsck.k9.crypto.MessageDecryptor;
|
import com.fsck.k9.crypto.MessageDecryptVerifyer;
|
||||||
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;
|
||||||
@ -20,7 +18,6 @@ import com.fsck.k9.mail.internet.MessageExtractor;
|
|||||||
import com.fsck.k9.mail.internet.MimeHeader;
|
import com.fsck.k9.mail.internet.MimeHeader;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.internet.Viewable;
|
import com.fsck.k9.mail.internet.Viewable;
|
||||||
import com.fsck.k9.mailstore.DecryptStreamParser.DecryptedBodyPart;
|
|
||||||
import com.fsck.k9.mailstore.MessageViewInfo.MessageViewContainer;
|
import com.fsck.k9.mailstore.MessageViewInfo.MessageViewContainer;
|
||||||
import com.fsck.k9.provider.AttachmentProvider;
|
import com.fsck.k9.provider.AttachmentProvider;
|
||||||
import org.openintents.openpgp.OpenPgpError;
|
import org.openintents.openpgp.OpenPgpError;
|
||||||
@ -443,7 +440,7 @@ public class LocalMessageExtractor {
|
|||||||
// TODO fill from part
|
// TODO fill from part
|
||||||
OpenPgpSignatureResult pgpResult = getSignatureResultForPart(part);
|
OpenPgpSignatureResult pgpResult = getSignatureResultForPart(part);
|
||||||
OpenPgpError pgpError = null;
|
OpenPgpError pgpError = null;
|
||||||
boolean wasEncrypted = false;
|
boolean wasEncrypted = getPartWasEncrypted(part);
|
||||||
PendingIntent pendingIntent = null;
|
PendingIntent pendingIntent = null;
|
||||||
|
|
||||||
containers.add(new MessageViewContainer(
|
containers.add(new MessageViewContainer(
|
||||||
@ -454,6 +451,10 @@ public class LocalMessageExtractor {
|
|||||||
return new MessageViewInfo(containers, message);
|
return new MessageViewInfo(containers, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean getPartWasEncrypted(Part part) {
|
||||||
|
return (part instanceof OpenPgpResultBodyPart) && ((OpenPgpResultBodyPart) part).wasEncrypted();
|
||||||
|
}
|
||||||
|
|
||||||
public static List<Part> getCryptPieces(Part part) throws MessagingException {
|
public static List<Part> getCryptPieces(Part part) throws MessagingException {
|
||||||
ArrayList<Part> parts = new ArrayList<Part>();
|
ArrayList<Part> parts = new ArrayList<Part>();
|
||||||
if (!getCryptSubPieces(part, parts)) {
|
if (!getCryptSubPieces(part, parts)) {
|
||||||
@ -477,6 +478,9 @@ public class LocalMessageExtractor {
|
|||||||
parts.add(part);
|
parts.add(part);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else if (MessageDecryptVerifyer.isPgpMimeSignedPart(part)) {
|
||||||
|
parts.add(part);
|
||||||
|
return true;
|
||||||
} else if (isPgpMimeDecryptedPart(part)) {
|
} else if (isPgpMimeDecryptedPart(part)) {
|
||||||
parts.add(multi.getBodyPart(2));
|
parts.add(multi.getBodyPart(2));
|
||||||
return true;
|
return true;
|
||||||
@ -488,14 +492,21 @@ public class LocalMessageExtractor {
|
|||||||
public static boolean isPgpMimeDecryptedPart (Part part) {
|
public static boolean isPgpMimeDecryptedPart (Part part) {
|
||||||
Body body = part.getBody();
|
Body body = part.getBody();
|
||||||
return (body instanceof Multipart)
|
return (body instanceof Multipart)
|
||||||
&& MessageDecryptor.isPgpMimeEncryptedPart(part)
|
&& MessageDecryptVerifyer.isPgpMimeEncryptedPart(part)
|
||||||
&& ((Multipart) part.getBody()).getBodyParts().size() == 3;
|
&& ((Multipart) part.getBody()).getCount() == 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OpenPgpSignatureResult getSignatureResultForPart(Part part) {
|
private static OpenPgpSignatureResult getSignatureResultForPart(Part part) {
|
||||||
if (part instanceof DecryptedBodyPart) {
|
if (part instanceof OpenPgpResultBodyPart) {
|
||||||
DecryptedBodyPart decryptedBodyPart = (DecryptedBodyPart) part;
|
OpenPgpResultBodyPart openPgpResultBodyPart = (OpenPgpResultBodyPart) part;
|
||||||
return decryptedBodyPart.getSignatureResult();
|
return openPgpResultBodyPart.getSignatureResult();
|
||||||
|
}
|
||||||
|
if (MessageDecryptVerifyer.isPgpMimeSignedPart(part)) {
|
||||||
|
Multipart multi = (Multipart) part.getBody();
|
||||||
|
if (multi.getCount() == 3 && multi.getBodyPart(2) instanceof OpenPgpResultBodyPart) {
|
||||||
|
OpenPgpResultBodyPart openPgpResultBodyPart = (OpenPgpResultBodyPart) multi.getBodyPart(2);
|
||||||
|
return openPgpResultBodyPart.getSignatureResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NO_SIGNATURE_RESULT;
|
return NO_SIGNATURE_RESULT;
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.fsck.k9.mailstore;
|
||||||
|
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
import com.fsck.k9.mail.internet.MimeBodyPart;
|
||||||
|
import org.openintents.openpgp.OpenPgpError;
|
||||||
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
|
|
||||||
|
|
||||||
|
public class OpenPgpResultBodyPart extends MimeBodyPart {
|
||||||
|
private boolean wasEncrypted;
|
||||||
|
private OpenPgpSignatureResult signatureResult;
|
||||||
|
private OpenPgpError error;
|
||||||
|
|
||||||
|
public OpenPgpResultBodyPart(boolean wasEncrypted) throws MessagingException {
|
||||||
|
this.wasEncrypted = wasEncrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenPgpSignatureResult getSignatureResult() {
|
||||||
|
return signatureResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignatureResult(OpenPgpSignatureResult signatureResult) {
|
||||||
|
this.signatureResult = signatureResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenPgpError getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError(OpenPgpError error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean wasEncrypted() {
|
||||||
|
return wasEncrypted;
|
||||||
|
}
|
||||||
|
}
|
@ -47,7 +47,7 @@ import com.fsck.k9.activity.MessageList;
|
|||||||
import com.fsck.k9.activity.MessageReference;
|
import com.fsck.k9.activity.MessageReference;
|
||||||
import com.fsck.k9.controller.MessagingController;
|
import com.fsck.k9.controller.MessagingController;
|
||||||
import com.fsck.k9.controller.MessagingListener;
|
import com.fsck.k9.controller.MessagingListener;
|
||||||
import com.fsck.k9.crypto.MessageDecryptor;
|
import com.fsck.k9.crypto.MessageDecryptVerifyer;
|
||||||
import com.fsck.k9.crypto.OpenPgpApiHelper;
|
import com.fsck.k9.crypto.OpenPgpApiHelper;
|
||||||
import com.fsck.k9.crypto.PgpData;
|
import com.fsck.k9.crypto.PgpData;
|
||||||
import com.fsck.k9.fragment.ConfirmationDialogFragment;
|
import com.fsck.k9.fragment.ConfirmationDialogFragment;
|
||||||
@ -64,7 +64,7 @@ import com.fsck.k9.mail.Multipart;
|
|||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
import com.fsck.k9.mailstore.AttachmentViewInfo;
|
import com.fsck.k9.mailstore.AttachmentViewInfo;
|
||||||
import com.fsck.k9.mailstore.DecryptStreamParser;
|
import com.fsck.k9.mailstore.DecryptStreamParser;
|
||||||
import com.fsck.k9.mailstore.DecryptStreamParser.DecryptedBodyPart;
|
import com.fsck.k9.mailstore.OpenPgpResultBodyPart;
|
||||||
import com.fsck.k9.mailstore.LocalMessage;
|
import com.fsck.k9.mailstore.LocalMessage;
|
||||||
import com.fsck.k9.mailstore.MessageViewInfo;
|
import com.fsck.k9.mailstore.MessageViewInfo;
|
||||||
import com.fsck.k9.ui.message.DecodeMessageLoader;
|
import com.fsck.k9.ui.message.DecodeMessageLoader;
|
||||||
@ -139,10 +139,10 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||||||
private LoaderCallbacks<MessageViewInfo> decodeMessageLoaderCallback = new DecodeMessageLoaderCallback();
|
private LoaderCallbacks<MessageViewInfo> decodeMessageLoaderCallback = new DecodeMessageLoaderCallback();
|
||||||
private MessageViewInfo messageViewInfo;
|
private MessageViewInfo messageViewInfo;
|
||||||
private AttachmentViewInfo currentAttachmentViewInfo;
|
private AttachmentViewInfo currentAttachmentViewInfo;
|
||||||
private Deque<Part> partsToDecrypt;
|
private Deque<Part> partsToDecryptOrVerify;
|
||||||
private OpenPgpApi openPgpApi;
|
private OpenPgpApi openPgpApi;
|
||||||
private Part currentlyDecryptingPart;
|
private Part currentlyDecrypringOrVerifyingPart;
|
||||||
private Intent currentDecryptingResult;
|
private Intent currentCryptoResult;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -257,38 +257,42 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||||||
if (message.isBodyMissing()) {
|
if (message.isBodyMissing()) {
|
||||||
startDownloadingMessageBody(message);
|
startDownloadingMessageBody(message);
|
||||||
} else {
|
} else {
|
||||||
decryptMessagePartsIfNecessary(message);
|
decryptOrVerifyMessagePartsIfNecessary(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decryptMessagePartsIfNecessary(LocalMessage message) {
|
private void decryptOrVerifyMessagePartsIfNecessary(LocalMessage message) {
|
||||||
List<Part> encryptedParts = MessageDecryptor.findEncryptedParts(message);
|
List<Part> encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message);
|
||||||
if (!encryptedParts.isEmpty()) {
|
List<Part> signedParts = MessageDecryptVerifyer.findSignedParts(message);
|
||||||
partsToDecrypt = new ArrayDeque<Part>(encryptedParts);
|
if (!encryptedParts.isEmpty() || !signedParts.isEmpty()) {
|
||||||
decryptNextPartOrStartExtractingTextAndAttachments();
|
partsToDecryptOrVerify = new ArrayDeque<Part>();
|
||||||
|
partsToDecryptOrVerify.addAll(encryptedParts);
|
||||||
|
partsToDecryptOrVerify.addAll(signedParts);
|
||||||
|
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||||
} else {
|
} else {
|
||||||
startExtractingTextAndAttachments(message);
|
startExtractingTextAndAttachments(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decryptNextPartOrStartExtractingTextAndAttachments() {
|
private void decryptOrVerifyNextPartOrStartExtractingTextAndAttachments() {
|
||||||
if (partsToDecrypt.isEmpty()) {
|
if (!partsToDecryptOrVerify.isEmpty()) {
|
||||||
startExtractingTextAndAttachments(mMessage);
|
|
||||||
|
Part part = partsToDecryptOrVerify.peekFirst();
|
||||||
|
if (MessageDecryptVerifyer.isPgpMimePart(part)) {
|
||||||
|
startDecryptingOrVerifyingPart(part);
|
||||||
|
} else {
|
||||||
|
partsToDecryptOrVerify.removeFirst();
|
||||||
|
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Part part = partsToDecrypt.peekFirst();
|
startExtractingTextAndAttachments(mMessage);
|
||||||
if (MessageDecryptor.isPgpMimeEncryptedPart(part)) {
|
|
||||||
startDecryptingPart(part);
|
|
||||||
} else {
|
|
||||||
// Note: We currently only support PGP/MIME multipart/encrypted parts
|
|
||||||
|
|
||||||
partsToDecrypt.removeFirst();
|
|
||||||
decryptNextPartOrStartExtractingTextAndAttachments();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startDecryptingPart(Part part) {
|
private void startDecryptingOrVerifyingPart(Part part) {
|
||||||
Multipart multipart = (Multipart) part.getBody();
|
Multipart multipart = (Multipart) part.getBody();
|
||||||
if (multipart == null) {
|
if (multipart == null) {
|
||||||
throw new RuntimeException("Downloading missing parts before decryption isn't supported yet");
|
throw new RuntimeException("Downloading missing parts before decryption isn't supported yet");
|
||||||
@ -297,7 +301,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||||||
if (!isBoundToCryptoProviderService()) {
|
if (!isBoundToCryptoProviderService()) {
|
||||||
connectToCryptoProviderService();
|
connectToCryptoProviderService();
|
||||||
} else {
|
} else {
|
||||||
decryptPart(part);
|
decryptOrVerifyPart(part);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +317,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||||||
public void onBound(IOpenPgpService service) {
|
public void onBound(IOpenPgpService service) {
|
||||||
openPgpApi = new OpenPgpApi(getContext(), service);
|
openPgpApi = new OpenPgpApi(getContext(), service);
|
||||||
|
|
||||||
decryptNextPartOrStartExtractingTextAndAttachments();
|
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -323,8 +327,8 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||||||
}).bindToService();
|
}).bindToService();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decryptPart(Part part) {
|
private void decryptOrVerifyPart(Part part) {
|
||||||
currentlyDecryptingPart = part;
|
currentlyDecrypringOrVerifyingPart = part;
|
||||||
decryptVerify(new Intent());
|
decryptVerify(new Intent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,24 +340,66 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||||||
intent.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, accountName);
|
intent.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, accountName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
|
|
||||||
PipedInputStream pipedInputStream = getPipedInputStreamForEncryptedData();
|
PipedInputStream pipedInputStream;
|
||||||
PipedOutputStream decryptedOutputStream = getPipedOutputStreamForDecryptedData(latch);
|
PipedOutputStream decryptedOutputStream;
|
||||||
|
final CountDownLatch latch;
|
||||||
|
|
||||||
|
if (MessageDecryptVerifyer.isPgpMimeSignedPart(currentlyDecrypringOrVerifyingPart)) {
|
||||||
|
pipedInputStream = getPipedInputStreamForSignedData();
|
||||||
|
|
||||||
|
byte[] signatureData = MessageDecryptVerifyer.getSignatureData(currentlyDecrypringOrVerifyingPart);
|
||||||
|
intent.putExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE, signatureData);
|
||||||
|
decryptedOutputStream = null;
|
||||||
|
latch = null;
|
||||||
|
} else {
|
||||||
|
pipedInputStream = getPipedInputStreamForEncryptedData();
|
||||||
|
latch = new CountDownLatch(1);
|
||||||
|
decryptedOutputStream = getPipedOutputStreamForDecryptedData(latch);
|
||||||
|
}
|
||||||
|
|
||||||
openPgpApi.executeApiAsync(intent, pipedInputStream, decryptedOutputStream, new IOpenPgpCallback() {
|
openPgpApi.executeApiAsync(intent, pipedInputStream, decryptedOutputStream, new IOpenPgpCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onReturn(Intent result) {
|
public void onReturn(Intent result) {
|
||||||
Log.d(K9.LOG_TAG, "on result!");
|
currentCryptoResult = result;
|
||||||
currentDecryptingResult = result;
|
|
||||||
latch.countDown();
|
if (latch != null) {
|
||||||
|
Log.d(K9.LOG_TAG, "on result!");
|
||||||
|
latch.countDown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onCryptoConverge(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(K9.LOG_TAG, "IOException", e);
|
Log.e(K9.LOG_TAG, "IOException", e);
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "MessagingException", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
private PipedInputStream getPipedInputStreamForEncryptedData() throws IOException {
|
||||||
PipedInputStream pipedInputStream = new PipedInputStream();
|
PipedInputStream pipedInputStream = new PipedInputStream();
|
||||||
|
|
||||||
@ -362,7 +408,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
Multipart multipartEncryptedMultipart = (Multipart) currentlyDecryptingPart.getBody();
|
Multipart multipartEncryptedMultipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody();
|
||||||
BodyPart encryptionPayloadPart = multipartEncryptedMultipart.getBodyPart(1);
|
BodyPart encryptionPayloadPart = multipartEncryptedMultipart.getBodyPart(1);
|
||||||
Body encryptionPayloadBody = encryptionPayloadPart.getBody();
|
Body encryptionPayloadBody = encryptionPayloadPart.getBody();
|
||||||
encryptionPayloadBody.writeTo(out);
|
encryptionPayloadBody.writeTo(out);
|
||||||
@ -378,10 +424,10 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||||||
private PipedOutputStream getPipedOutputStreamForDecryptedData(final CountDownLatch latch) throws IOException {
|
private PipedOutputStream getPipedOutputStreamForDecryptedData(final CountDownLatch latch) throws IOException {
|
||||||
PipedOutputStream decryptedOutputStream = new PipedOutputStream();
|
PipedOutputStream decryptedOutputStream = new PipedOutputStream();
|
||||||
final PipedInputStream decryptedInputStream = new PipedInputStream(decryptedOutputStream);
|
final PipedInputStream decryptedInputStream = new PipedInputStream(decryptedOutputStream);
|
||||||
new AsyncTask<Void, Void, DecryptedBodyPart>() {
|
new AsyncTask<Void, Void, OpenPgpResultBodyPart>() {
|
||||||
@Override
|
@Override
|
||||||
protected DecryptedBodyPart doInBackground(Void... params) {
|
protected OpenPgpResultBodyPart doInBackground(Void... params) {
|
||||||
DecryptedBodyPart decryptedPart = null;
|
OpenPgpResultBodyPart decryptedPart = null;
|
||||||
try {
|
try {
|
||||||
decryptedPart = DecryptStreamParser.parse(decryptedInputStream);
|
decryptedPart = DecryptStreamParser.parse(decryptedInputStream);
|
||||||
|
|
||||||
@ -396,21 +442,21 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(DecryptedBodyPart decryptedPart) {
|
protected void onPostExecute(OpenPgpResultBodyPart decryptedPart) {
|
||||||
onDecryptionConverge(decryptedPart);
|
onCryptoConverge(decryptedPart);
|
||||||
}
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
return decryptedOutputStream;
|
return decryptedOutputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDecryptionConverge (DecryptedBodyPart decryptedPart) {
|
private void onCryptoConverge(OpenPgpResultBodyPart openPgpResultBodyPart) {
|
||||||
try {
|
try {
|
||||||
if (currentDecryptingResult == null) {
|
if (currentCryptoResult == null) {
|
||||||
Log.e(K9.LOG_TAG, "Internal error: we should have a result here!");
|
Log.e(K9.LOG_TAG, "Internal error: we should have a result here!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int resultCode = currentDecryptingResult.getIntExtra(OpenPgpApi.RESULT_CODE, INVALID_OPENPGP_RESULT_CODE);
|
int resultCode = currentCryptoResult.getIntExtra(OpenPgpApi.RESULT_CODE, INVALID_OPENPGP_RESULT_CODE);
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.d(K9.LOG_TAG, "OpenPGP API decryptVerify result code: " + resultCode);
|
Log.d(K9.LOG_TAG, "OpenPGP API decryptVerify result code: " + resultCode);
|
||||||
}
|
}
|
||||||
@ -421,7 +467,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
|
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
|
||||||
PendingIntent pendingIntent = currentDecryptingResult.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
|
PendingIntent pendingIntent = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
|
||||||
if (pendingIntent == null) {
|
if (pendingIntent == null) {
|
||||||
throw new AssertionError("Expecting PendingIntent on USER_INTERACTION_REQUIRED!");
|
throw new AssertionError("Expecting PendingIntent on USER_INTERACTION_REQUIRED!");
|
||||||
}
|
}
|
||||||
@ -435,62 +481,68 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpenPgpApi.RESULT_CODE_ERROR: {
|
case OpenPgpApi.RESULT_CODE_ERROR: {
|
||||||
OpenPgpError error = currentDecryptingResult.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
|
OpenPgpError error = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
|
||||||
|
|
||||||
if (K9.DEBUG) {
|
if (K9.DEBUG) {
|
||||||
Log.w(K9.LOG_TAG, "OpenPGP API error: " + error.getMessage());
|
Log.w(K9.LOG_TAG, "OpenPGP API error: " + error.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
onDecryptionFailed(error);
|
onCryptoFailed(error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
||||||
|
if (openPgpResultBodyPart == null) {
|
||||||
|
openPgpResultBodyPart = new OpenPgpResultBodyPart(false);
|
||||||
|
}
|
||||||
OpenPgpSignatureResult signatureResult =
|
OpenPgpSignatureResult signatureResult =
|
||||||
currentDecryptingResult.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
|
currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
|
||||||
decryptedPart.setSignatureResult(signatureResult);
|
openPgpResultBodyPart.setSignatureResult(signatureResult);
|
||||||
|
|
||||||
onDecryptionSuccess(decryptedPart);
|
onCryptoSuccess(openPgpResultBodyPart);
|
||||||
break;
|
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 {
|
} finally {
|
||||||
currentDecryptingResult = null;
|
currentCryptoResult = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleCryptoResult(int resultCode, Intent data) {
|
public void handleCryptoResult(int resultCode, Intent data) {
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
decryptNextPartOrStartExtractingTextAndAttachments();
|
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||||
} else {
|
} else {
|
||||||
//FIXME: don't pass null
|
//FIXME: don't pass null
|
||||||
onDecryptionFailed(null);
|
onCryptoFailed(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDecryptionSuccess(DecryptedBodyPart decryptedPart) {
|
private void onCryptoSuccess(OpenPgpResultBodyPart decryptedPart) {
|
||||||
addDecryptedPartToMessage(decryptedPart);
|
addOpenPgpResultPartToMessage(decryptedPart);
|
||||||
onDecryptionFinished();
|
onCryptoFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDecryptedPartToMessage(DecryptedBodyPart decryptedPart) {
|
private void addOpenPgpResultPartToMessage(OpenPgpResultBodyPart decryptedPart) {
|
||||||
Multipart multipart = (Multipart) currentlyDecryptingPart.getBody();
|
Multipart multipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody();
|
||||||
multipart.addBodyPart(decryptedPart);
|
multipart.addBodyPart(decryptedPart);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDecryptionFailed(OpenPgpError error) {
|
private void onCryptoFailed(OpenPgpError error) {
|
||||||
try {
|
try {
|
||||||
DecryptedBodyPart errorPart = new DecryptedBodyPart();
|
OpenPgpResultBodyPart errorPart = new OpenPgpResultBodyPart(false);
|
||||||
errorPart.setError(error);
|
errorPart.setError(error);
|
||||||
addDecryptedPartToMessage(errorPart);
|
addOpenPgpResultPartToMessage(errorPart);
|
||||||
} catch (MessagingException e) {
|
} catch (MessagingException e) {
|
||||||
Log.e(K9.LOG_TAG, "This shouldn't happen", e);
|
Log.e(K9.LOG_TAG, "This shouldn't happen", e);
|
||||||
}
|
}
|
||||||
onDecryptionFinished();
|
onCryptoFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDecryptionFinished() {
|
private void onCryptoFinished() {
|
||||||
partsToDecrypt.removeFirst();
|
partsToDecryptOrVerify.removeFirst();
|
||||||
decryptNextPartOrStartExtractingTextAndAttachments();
|
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onLoadMessageFromDatabaseFailed() {
|
private void onLoadMessageFromDatabaseFailed() {
|
||||||
@ -527,7 +579,6 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onDecodeMessageFinished(MessageViewInfo messageContainer) {
|
private void onDecodeMessageFinished(MessageViewInfo messageContainer) {
|
||||||
//TODO: handle decryption and signature verification
|
|
||||||
this.messageViewInfo = messageContainer;
|
this.messageViewInfo = messageContainer;
|
||||||
showMessage(messageContainer);
|
showMessage(messageContainer);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user