convert ComposeCryptoStatus to kotlin
This commit is contained in:
parent
e1015d325b
commit
6a19eb3d3a
10 changed files with 877 additions and 989 deletions
|
@ -5,6 +5,7 @@ import java.io.InputStream;
|
|||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.support.annotation.WorkerThread;
|
||||
|
@ -51,7 +52,7 @@ public class AutocryptStatusInteractor {
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NonNull
|
||||
private RecipientAutocryptStatusType getRecipientAutocryptStatusFromIntent(Intent result) {
|
||||
boolean allKeysConfirmed = result.getBooleanExtra(OpenPgpApi.RESULT_KEYS_CONFIRMED, false);
|
||||
int autocryptStatus =
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
package com.fsck.k9.message
|
||||
|
||||
interface CryptoStatus {
|
||||
fun getOpenPgpKeyId(): Long?
|
||||
val openPgpKeyId: Long?
|
||||
fun isProviderStateOk(): Boolean
|
||||
fun isSenderPreferEncryptMutual(): Boolean
|
||||
val isSenderPreferEncryptMutual: Boolean
|
||||
val isEncryptionEnabled: Boolean
|
||||
val isPgpInlineModeEnabled: Boolean
|
||||
val isSignOnly: Boolean
|
||||
fun isSigningEnabled(): Boolean
|
||||
fun isEncryptionEnabled(): Boolean
|
||||
fun isPgpInlineModeEnabled(): Boolean
|
||||
fun isSignOnly(): Boolean
|
||||
fun isUserChoice(): Boolean
|
||||
fun isReplyToEncrypted(): Boolean
|
||||
val isReplyToEncrypted: Boolean
|
||||
fun hasRecipients(): Boolean
|
||||
fun isEncryptSubject(): Boolean
|
||||
val isEncryptSubject: Boolean
|
||||
fun getRecipientAddresses(): Array<String>
|
||||
}
|
||||
|
|
|
@ -1,623 +0,0 @@
|
|||
package com.fsck.k9.message;
|
||||
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.fsck.k9.Account.QuoteStyle;
|
||||
import com.fsck.k9.Identity;
|
||||
import com.fsck.k9.K9RobolectricTest;
|
||||
import com.fsck.k9.TestCoreResourceProvider;
|
||||
import com.fsck.k9.autocrypt.AutocryptOpenPgpApiInteractor;
|
||||
import com.fsck.k9.autocrypt.AutocryptOperations;
|
||||
import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.mail.BodyPart;
|
||||
import com.fsck.k9.mail.BoundaryGenerator;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
||||
import com.fsck.k9.mail.internet.MessageIdGenerator;
|
||||
import com.fsck.k9.mail.internet.MimeMessage;
|
||||
import com.fsck.k9.mail.internet.MimeMultipart;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.internet.TextBody;
|
||||
import com.fsck.k9.message.MessageBuilder.Callback;
|
||||
import com.fsck.k9.message.quote.InsertableHtmlContent;
|
||||
import org.apache.commons.io.Charsets;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.james.mime4j.util.MimeUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.openintents.openpgp.util.OpenPgpApi;
|
||||
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource;
|
||||
|
||||
import static com.fsck.k9.autocrypt.AutocryptOperationsHelper.assertMessageHasAutocryptHeader;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
public class PgpMessageBuilderTest extends K9RobolectricTest {
|
||||
private static final long TEST_KEY_ID = 123L;
|
||||
private static final String TEST_MESSAGE_TEXT = "message text with a ☭ CCCP symbol";
|
||||
private static final byte[] AUTOCRYPT_KEY_MATERIAL = { 1, 2, 3 };
|
||||
private static final String SENDER_EMAIL = "test@example.org";
|
||||
|
||||
|
||||
private CryptoStatus cryptoStatus = createCryptoStatus();
|
||||
private OpenPgpApi openPgpApi = mock(OpenPgpApi.class);
|
||||
private AutocryptOpenPgpApiInteractor autocryptOpenPgpApiInteractor = mock(AutocryptOpenPgpApiInteractor.class);
|
||||
private PgpMessageBuilder pgpMessageBuilder = createDefaultPgpMessageBuilder(openPgpApi,
|
||||
autocryptOpenPgpApiInteractor, cryptoStatus);
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
when(autocryptOpenPgpApiInteractor.getKeyMaterialForKeyId(openPgpApi, TEST_KEY_ID, SENDER_EMAIL))
|
||||
.thenReturn(AUTOCRYPT_KEY_MATERIAL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void build__withCryptoProviderNotOk__shouldThrow() {
|
||||
configureEncryptAndSign();
|
||||
when(cryptoStatus.isProviderStateOk()).thenReturn(false);
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any(MessagingException.class));
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildCleartext__withNoSigningKey__shouldBuildTrivialMessage() {
|
||||
configureCleartext();
|
||||
when(cryptoStatus.getOpenPgpKeyId()).thenReturn(null);
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
ArgumentCaptor<MimeMessage> captor = ArgumentCaptor.forClass(MimeMessage.class);
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false));
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
|
||||
MimeMessage message = captor.getValue();
|
||||
assertEquals("text/plain", message.getMimeType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildCleartext__shouldSucceed() {
|
||||
configureCleartext();
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
ArgumentCaptor<MimeMessage> captor = ArgumentCaptor.forClass(MimeMessage.class);
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false));
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
|
||||
MimeMessage message = captor.getValue();
|
||||
assertMessageHasAutocryptHeader(message, SENDER_EMAIL, false, AUTOCRYPT_KEY_MATERIAL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildSign__withNoDetachedSignatureInResult__shouldThrow() {
|
||||
configureSignOnly();
|
||||
|
||||
Intent returnIntent = new Intent();
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
when(openPgpApi.executeApi(any(Intent.class), any(OpenPgpDataSource.class), nullable(OutputStream.class)))
|
||||
.thenReturn(returnIntent);
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any(MessagingException.class));
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildSign__withDetachedSignatureInResult__shouldSucceed() {
|
||||
configureSignOnly();
|
||||
|
||||
ArgumentCaptor<Intent> capturedApiIntent = ArgumentCaptor.forClass(Intent.class);
|
||||
|
||||
Intent returnIntent = new Intent();
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_DETACHED_SIGNATURE, new byte[] { 1, 2, 3 });
|
||||
when(openPgpApi.executeApi(capturedApiIntent.capture(), any(OpenPgpDataSource.class),
|
||||
nullable(OutputStream.class))).thenReturn(returnIntent);
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
Intent expectedIntent = new Intent(OpenPgpApi.ACTION_DETACHED_SIGN);
|
||||
expectedIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, TEST_KEY_ID);
|
||||
expectedIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
assertIntentEqualsActionAndExtras(expectedIntent, capturedApiIntent.getValue());
|
||||
|
||||
ArgumentCaptor<MimeMessage> captor = ArgumentCaptor.forClass(MimeMessage.class);
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false));
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
|
||||
MimeMessage message = captor.getValue();
|
||||
Assert.assertEquals("message must be multipart/signed", "multipart/signed", message.getMimeType());
|
||||
|
||||
MimeMultipart multipart = (MimeMultipart) message.getBody();
|
||||
Assert.assertEquals("multipart/signed must consist of two parts", 2, multipart.getCount());
|
||||
|
||||
BodyPart contentBodyPart = multipart.getBodyPart(0);
|
||||
Assert.assertEquals("first part must have content type text/plain",
|
||||
"text/plain", MimeUtility.getHeaderParameter(contentBodyPart.getContentType(), null));
|
||||
assertTrue("signed message body must be TextBody", contentBodyPart.getBody() instanceof TextBody);
|
||||
Assert.assertEquals(MimeUtil.ENC_QUOTED_PRINTABLE, ((TextBody) contentBodyPart.getBody()).getEncoding());
|
||||
assertContentOfBodyPartEquals("content must match the message text", contentBodyPart, TEST_MESSAGE_TEXT);
|
||||
|
||||
BodyPart signatureBodyPart = multipart.getBodyPart(1);
|
||||
String contentType = signatureBodyPart.getContentType();
|
||||
Assert.assertEquals("second part must be pgp signature", "application/pgp-signature",
|
||||
MimeUtility.getHeaderParameter(contentType, null));
|
||||
Assert.assertEquals("second part must be called signature.asc", "signature.asc",
|
||||
MimeUtility.getHeaderParameter(contentType, "name"));
|
||||
assertContentOfBodyPartEquals("content must match the supplied detached signature",
|
||||
signatureBodyPart, new byte[] { 1, 2, 3 });
|
||||
|
||||
assertMessageHasAutocryptHeader(message, SENDER_EMAIL, false, AUTOCRYPT_KEY_MATERIAL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildSign__withUserInteractionResult__shouldReturnUserInteraction() {
|
||||
configureSignOnly();
|
||||
|
||||
Intent returnIntent = mock(Intent.class);
|
||||
when(returnIntent.getIntExtra(eq(OpenPgpApi.RESULT_CODE), anyInt()))
|
||||
.thenReturn(OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||
final PendingIntent mockPendingIntent = mock(PendingIntent.class);
|
||||
when(returnIntent.getParcelableExtra(eq(OpenPgpApi.RESULT_INTENT)))
|
||||
.thenReturn(mockPendingIntent);
|
||||
|
||||
when(openPgpApi.executeApi(any(Intent.class), any(OpenPgpDataSource.class), nullable(OutputStream.class)))
|
||||
.thenReturn(returnIntent);
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
|
||||
verify(mockCallback).onMessageBuildReturnPendingIntent(captor.capture(), anyInt());
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
|
||||
PendingIntent pendingIntent = captor.getValue();
|
||||
Assert.assertSame(pendingIntent, mockPendingIntent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildSign__withReturnAfterUserInteraction__shouldSucceed() {
|
||||
configureSignOnly();
|
||||
|
||||
int returnedRequestCode;
|
||||
{
|
||||
Intent returnIntent = spy(new Intent());
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||
|
||||
PendingIntent mockPendingIntent = mock(PendingIntent.class);
|
||||
when(returnIntent.getParcelableExtra(eq(OpenPgpApi.RESULT_INTENT)))
|
||||
.thenReturn(mockPendingIntent);
|
||||
|
||||
when(openPgpApi.executeApi(any(Intent.class), any(OpenPgpDataSource.class), nullable(OutputStream.class)))
|
||||
.thenReturn(returnIntent);
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
verify(returnIntent).getIntExtra(eq(OpenPgpApi.RESULT_CODE), anyInt());
|
||||
ArgumentCaptor<PendingIntent> piCaptor = ArgumentCaptor.forClass(PendingIntent.class);
|
||||
ArgumentCaptor<Integer> rcCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||
verify(mockCallback).onMessageBuildReturnPendingIntent(piCaptor.capture(), rcCaptor.capture());
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
|
||||
returnedRequestCode = rcCaptor.getValue();
|
||||
Assert.assertSame(mockPendingIntent, piCaptor.getValue());
|
||||
}
|
||||
|
||||
{
|
||||
Intent returnIntent = spy(new Intent());
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
|
||||
Intent mockReturnIntent = mock(Intent.class);
|
||||
when(openPgpApi.executeApi(same(mockReturnIntent), any(OpenPgpDataSource.class),
|
||||
nullable(OutputStream.class))).thenReturn(returnIntent);
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.onActivityResult(returnedRequestCode, Activity.RESULT_OK, mockReturnIntent, mockCallback);
|
||||
verify(openPgpApi).executeApi(same(mockReturnIntent), any(OpenPgpDataSource.class),
|
||||
nullable(OutputStream.class));
|
||||
verify(returnIntent).getIntExtra(eq(OpenPgpApi.RESULT_CODE), anyInt());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildEncrypt__withoutRecipients__shouldThrow() {
|
||||
configureEncryptAndSign();
|
||||
|
||||
Intent returnIntent = spy(new Intent());
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
when(openPgpApi.executeApi(any(Intent.class), any(OpenPgpDataSource.class), nullable(OutputStream.class)))
|
||||
.thenReturn(returnIntent);
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any(MessagingException.class));
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildEncrypt__checkGossip() {
|
||||
configureEncryptAndSign();
|
||||
configureRecipients("alice@example.org", "bob@example.org");
|
||||
|
||||
Intent returnIntent = new Intent();
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
when(openPgpApi.executeApi(any(Intent.class), any(OpenPgpDataSource.class), nullable(OutputStream.class)))
|
||||
.thenReturn(returnIntent);
|
||||
pgpMessageBuilder.buildAsync(mock(Callback.class));
|
||||
|
||||
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForUserId(same(openPgpApi), eq("alice@example.org"));
|
||||
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForUserId(same(openPgpApi), eq("bob@example.org"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildEncrypt__checkGossip__filterBcc() {
|
||||
configureEncryptAndSign();
|
||||
configureRecipients("alice@example.org", "bob@example.org", "carol@example.org");
|
||||
pgpMessageBuilder.setBcc(Collections.singletonList(new Address("carol@example.org")));
|
||||
|
||||
Intent returnIntent = new Intent();
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
when(openPgpApi.executeApi(any(Intent.class), any(OpenPgpDataSource.class), nullable(OutputStream.class)))
|
||||
.thenReturn(returnIntent);
|
||||
pgpMessageBuilder.buildAsync(mock(Callback.class));
|
||||
|
||||
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForUserId(same(openPgpApi), eq("alice@example.org"));
|
||||
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForUserId(same(openPgpApi), eq("bob@example.org"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildEncrypt__checkGossip__filterBccSingleRecipient() {
|
||||
configureEncryptAndSign();
|
||||
configureRecipients("alice@example.org", "carol@example.org");
|
||||
pgpMessageBuilder.setBcc(Collections.singletonList(new Address("carol@example.org")));
|
||||
|
||||
Intent returnIntent = new Intent();
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
when(openPgpApi.executeApi(any(Intent.class), any(OpenPgpDataSource.class), nullable(OutputStream.class)))
|
||||
.thenReturn(returnIntent);
|
||||
pgpMessageBuilder.buildAsync(mock(Callback.class));
|
||||
|
||||
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForKeyId(any(OpenPgpApi.class), any(Long.class), any(String.class));
|
||||
verifyNoMoreInteractions(autocryptOpenPgpApiInteractor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildEncrypt__shouldSucceed() {
|
||||
configureEncryptAndSign();
|
||||
configureRecipients("test@example.org");
|
||||
|
||||
ArgumentCaptor<Intent> capturedApiIntent = ArgumentCaptor.forClass(Intent.class);
|
||||
|
||||
Intent returnIntent = new Intent();
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
|
||||
when(openPgpApi.executeApi(capturedApiIntent.capture(), any(OpenPgpDataSource.class),
|
||||
nullable(OutputStream.class))).thenReturn(returnIntent);
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
Intent expectedApiIntent = new Intent(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT);
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, TEST_KEY_ID);
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, new long[] { TEST_KEY_ID });
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_USER_IDS, cryptoStatus.getRecipientAddresses());
|
||||
assertIntentEqualsActionAndExtras(expectedApiIntent, capturedApiIntent.getValue());
|
||||
|
||||
ArgumentCaptor<MimeMessage> captor = ArgumentCaptor.forClass(MimeMessage.class);
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false));
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
|
||||
MimeMessage message = captor.getValue();
|
||||
|
||||
Assert.assertEquals("message must be multipart/encrypted", "multipart/encrypted", message.getMimeType());
|
||||
|
||||
MimeMultipart multipart = (MimeMultipart) message.getBody();
|
||||
Assert.assertEquals("multipart/encrypted must consist of two parts", 2, multipart.getCount());
|
||||
|
||||
BodyPart dummyBodyPart = multipart.getBodyPart(0);
|
||||
Assert.assertEquals("first part must be pgp encrypted dummy part",
|
||||
"application/pgp-encrypted", dummyBodyPart.getContentType());
|
||||
assertContentOfBodyPartEquals("content must match the supplied detached signature",
|
||||
dummyBodyPart, "Version: 1");
|
||||
|
||||
BodyPart encryptedBodyPart = multipart.getBodyPart(1);
|
||||
Assert.assertEquals("second part must be octet-stream of encrypted data",
|
||||
"application/octet-stream; name=\"encrypted.asc\"", encryptedBodyPart.getContentType());
|
||||
assertTrue("message body must be BinaryTempFileBody",
|
||||
encryptedBodyPart.getBody() instanceof BinaryTempFileBody);
|
||||
Assert.assertEquals(MimeUtil.ENC_7BIT, ((BinaryTempFileBody) encryptedBodyPart.getBody()).getEncoding());
|
||||
|
||||
assertMessageHasAutocryptHeader(message, SENDER_EMAIL, false, AUTOCRYPT_KEY_MATERIAL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildEncrypt__withInlineEnabled__shouldSucceed() {
|
||||
configureEncryptAndSign();
|
||||
configureRecipients("test@example.org");
|
||||
configurePgpInline();
|
||||
|
||||
ArgumentCaptor<Intent> capturedApiIntent = ArgumentCaptor.forClass(Intent.class);
|
||||
|
||||
Intent returnIntent = new Intent();
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
|
||||
when(openPgpApi.executeApi(capturedApiIntent.capture(), any(OpenPgpDataSource.class),
|
||||
nullable(OutputStream.class))).thenReturn(returnIntent);
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
Intent expectedApiIntent = new Intent(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT);
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, TEST_KEY_ID);
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, new long[] { TEST_KEY_ID });
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_USER_IDS, cryptoStatus.getRecipientAddresses());
|
||||
assertIntentEqualsActionAndExtras(expectedApiIntent, capturedApiIntent.getValue());
|
||||
|
||||
ArgumentCaptor<MimeMessage> captor = ArgumentCaptor.forClass(MimeMessage.class);
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false));
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
|
||||
MimeMessage message = captor.getValue();
|
||||
Assert.assertEquals("text/plain", message.getMimeType());
|
||||
assertTrue("message body must be BinaryTempFileBody", message.getBody() instanceof BinaryTempFileBody);
|
||||
Assert.assertEquals(MimeUtil.ENC_7BIT, ((BinaryTempFileBody) message.getBody()).getEncoding());
|
||||
|
||||
assertMessageHasAutocryptHeader(message, SENDER_EMAIL, false, AUTOCRYPT_KEY_MATERIAL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildSign__withInlineEnabled__shouldSucceed() {
|
||||
configureSignOnly();
|
||||
configureRecipients("test@example.org");
|
||||
configurePgpInline();
|
||||
|
||||
ArgumentCaptor<Intent> capturedApiIntent = ArgumentCaptor.forClass(Intent.class);
|
||||
|
||||
Intent returnIntent = new Intent();
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
|
||||
when(openPgpApi.executeApi(capturedApiIntent.capture(), any(OpenPgpDataSource.class),
|
||||
nullable(OutputStream.class))).thenReturn(returnIntent);
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
Intent expectedApiIntent = new Intent(OpenPgpApi.ACTION_SIGN);
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, TEST_KEY_ID);
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
assertIntentEqualsActionAndExtras(expectedApiIntent, capturedApiIntent.getValue());
|
||||
|
||||
ArgumentCaptor<MimeMessage> captor = ArgumentCaptor.forClass(MimeMessage.class);
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false));
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
|
||||
MimeMessage message = captor.getValue();
|
||||
Assert.assertEquals("message must be text/plain", "text/plain", message.getMimeType());
|
||||
|
||||
assertMessageHasAutocryptHeader(message, SENDER_EMAIL, false, AUTOCRYPT_KEY_MATERIAL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildSignWithAttach__withInlineEnabled__shouldThrow() {
|
||||
configureSignOnly();
|
||||
configurePgpInline();
|
||||
pgpMessageBuilder.setAttachments(createAttachmentList());
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any(MessagingException.class));
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
verifyNoMoreInteractions(openPgpApi);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildEncryptWithAttach__withInlineEnabled__shouldThrow() {
|
||||
configureEncryptAndSign();
|
||||
configurePgpInline();
|
||||
pgpMessageBuilder.setAttachments(createAttachmentList());
|
||||
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any(MessagingException.class));
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
verifyNoMoreInteractions(openPgpApi);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildSign__withNoDetachedSignatureExtra__shouldFail() {
|
||||
configureSignOnly();
|
||||
|
||||
Intent returnIntentSigned = new Intent();
|
||||
returnIntentSigned.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
// no OpenPgpApi.EXTRA_DETACHED_SIGNATURE!
|
||||
|
||||
|
||||
when(openPgpApi.executeApi(any(Intent.class), any(OpenPgpDataSource.class), nullable(OutputStream.class)))
|
||||
.thenReturn(returnIntentSigned);
|
||||
Callback mockCallback = mock(Callback.class);
|
||||
pgpMessageBuilder.buildAsync(mockCallback);
|
||||
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any(MessagingException.class));
|
||||
verifyNoMoreInteractions(mockCallback);
|
||||
}
|
||||
|
||||
private CryptoStatus createCryptoStatus() {
|
||||
CryptoStatus cryptoStatus = mock(CryptoStatus.class);
|
||||
when(cryptoStatus.isPgpInlineModeEnabled()).thenReturn(false);
|
||||
when(cryptoStatus.isSenderPreferEncryptMutual()).thenReturn(false);
|
||||
when(cryptoStatus.isEncryptSubject()).thenReturn(true);
|
||||
when(cryptoStatus.getOpenPgpKeyId()).thenReturn(TEST_KEY_ID);
|
||||
when(cryptoStatus.getRecipientAddresses()).thenReturn(new String[0]);
|
||||
when(cryptoStatus.hasRecipients()).thenReturn(false);
|
||||
when(cryptoStatus.isProviderStateOk()).thenReturn(true);
|
||||
return cryptoStatus;
|
||||
}
|
||||
|
||||
private void configureEncryptAndSign() {
|
||||
when(cryptoStatus.isEncryptionEnabled()).thenReturn(true);
|
||||
when(cryptoStatus.isSigningEnabled()).thenReturn(true);
|
||||
}
|
||||
|
||||
private void configureSignOnly() {
|
||||
when(cryptoStatus.isEncryptionEnabled()).thenReturn(false);
|
||||
when(cryptoStatus.isSigningEnabled()).thenReturn(true);
|
||||
}
|
||||
|
||||
private void configureCleartext() {
|
||||
when(cryptoStatus.isEncryptionEnabled()).thenReturn(false);
|
||||
when(cryptoStatus.isSigningEnabled()).thenReturn(false);
|
||||
}
|
||||
|
||||
private void configurePgpInline() {
|
||||
when(cryptoStatus.isPgpInlineModeEnabled()).thenReturn(true);
|
||||
}
|
||||
|
||||
private void configureRecipients(String... recipients) {
|
||||
when(cryptoStatus.hasRecipients()).thenReturn(true);
|
||||
when(cryptoStatus.getRecipientAddresses()).thenReturn(recipients);
|
||||
}
|
||||
|
||||
private static PgpMessageBuilder createDefaultPgpMessageBuilder(OpenPgpApi openPgpApi,
|
||||
AutocryptOpenPgpApiInteractor autocryptOpenPgpApiInteractor, CryptoStatus cryptoStatus) {
|
||||
PgpMessageBuilder builder = new PgpMessageBuilder(
|
||||
MessageIdGenerator.getInstance(), BoundaryGenerator.getInstance(),
|
||||
AutocryptOperations.getInstance(), autocryptOpenPgpApiInteractor, new TestCoreResourceProvider());
|
||||
builder.setOpenPgpApi(openPgpApi);
|
||||
builder.setCryptoStatus(cryptoStatus);
|
||||
|
||||
Identity identity = new Identity();
|
||||
identity.setName("tester");
|
||||
identity.setEmail(SENDER_EMAIL);
|
||||
identity.setDescription("test identity");
|
||||
identity.setSignatureUse(false);
|
||||
|
||||
builder.setSubject("subject")
|
||||
.setSentDate(new Date())
|
||||
.setHideTimeZone(false)
|
||||
.setTo(new ArrayList<Address>())
|
||||
.setCc(new ArrayList<Address>())
|
||||
.setBcc(new ArrayList<Address>())
|
||||
.setInReplyTo("inreplyto")
|
||||
.setReferences("references")
|
||||
.setRequestReadReceipt(false)
|
||||
.setIdentity(identity)
|
||||
.setMessageFormat(SimpleMessageFormat.TEXT)
|
||||
.setText(TEST_MESSAGE_TEXT)
|
||||
.setAttachments(new ArrayList<Attachment>())
|
||||
.setSignature("signature")
|
||||
.setQuoteStyle(QuoteStyle.PREFIX)
|
||||
.setQuotedTextMode(QuotedTextMode.NONE)
|
||||
.setQuotedText("quoted text")
|
||||
.setQuotedHtmlContent(new InsertableHtmlContent())
|
||||
.setReplyAfterQuote(false)
|
||||
.setSignatureBeforeQuotedText(false)
|
||||
.setIdentityChanged(false)
|
||||
.setSignatureChanged(false)
|
||||
.setCursorPosition(0)
|
||||
.setMessageReference(null)
|
||||
.setDraft(false);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static List<Attachment> createAttachmentList() {
|
||||
Attachment attachment = mock(Attachment.class);
|
||||
when(attachment.getState()).thenReturn(Attachment.LoadingState.URI_ONLY);
|
||||
return Collections.singletonList(attachment);
|
||||
}
|
||||
|
||||
private static void assertContentOfBodyPartEquals(String reason, BodyPart signatureBodyPart, byte[] expected) {
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
signatureBodyPart.getBody().writeTo(bos);
|
||||
Assert.assertArrayEquals(reason, expected, bos.toByteArray());
|
||||
} catch (IOException | MessagingException e) {
|
||||
Assert.fail();
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertContentOfBodyPartEquals(String reason, BodyPart signatureBodyPart, String expected) {
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
InputStream inputStream = MimeUtility.decodeBody(signatureBodyPart.getBody());
|
||||
IOUtils.copy(inputStream, bos);
|
||||
Assert.assertEquals(reason, expected, new String(bos.toByteArray(), Charsets.UTF_8));
|
||||
} catch (IOException | MessagingException e) {
|
||||
Assert.fail();
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertIntentEqualsActionAndExtras(Intent expected, Intent actual) {
|
||||
Assert.assertEquals(expected.getAction(), actual.getAction());
|
||||
|
||||
Bundle expectedExtras = expected.getExtras();
|
||||
Bundle intentExtras = actual.getExtras();
|
||||
|
||||
if (expectedExtras.size() != intentExtras.size()) {
|
||||
Assert.assertEquals(expectedExtras.size(), intentExtras.size());
|
||||
}
|
||||
|
||||
for (String key : expectedExtras.keySet()) {
|
||||
Object intentExtra = intentExtras.get(key);
|
||||
Object expectedExtra = expectedExtras.get(key);
|
||||
if (intentExtra == null) {
|
||||
if (expectedExtra == null) {
|
||||
continue;
|
||||
}
|
||||
Assert.fail("found null for an expected non-null extra: " + key);
|
||||
}
|
||||
if (intentExtra instanceof long[]) {
|
||||
if (!Arrays.equals((long[]) intentExtra, (long[]) expectedExtra)) {
|
||||
Assert.assertArrayEquals("error in " + key, (long[]) expectedExtra, (long[]) intentExtra);
|
||||
}
|
||||
} else {
|
||||
if (!intentExtra.equals(expectedExtra)) {
|
||||
Assert.assertEquals("error in " + key, expectedExtra, intentExtra);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,672 @@
|
|||
package com.fsck.k9.message
|
||||
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import android.os.Parcelable
|
||||
import com.fsck.k9.Account.QuoteStyle
|
||||
import com.fsck.k9.Identity
|
||||
import com.fsck.k9.RobolectricTest
|
||||
import com.fsck.k9.activity.compose.ComposeCryptoStatus
|
||||
import com.fsck.k9.activity.compose.RecipientPresenter.CryptoMode
|
||||
import com.fsck.k9.activity.misc.Attachment
|
||||
import com.fsck.k9.autocrypt.AutocryptOpenPgpApiInteractor
|
||||
import com.fsck.k9.autocrypt.AutocryptOperations
|
||||
import com.fsck.k9.autocrypt.AutocryptOperationsHelper.assertMessageHasAutocryptHeader
|
||||
import com.fsck.k9.mail.Address
|
||||
import com.fsck.k9.mail.BodyPart
|
||||
import com.fsck.k9.mail.BoundaryGenerator
|
||||
import com.fsck.k9.mail.MessagingException
|
||||
import com.fsck.k9.mail.internet.*
|
||||
import com.fsck.k9.message.MessageBuilder.Callback
|
||||
import com.fsck.k9.message.quote.InsertableHtmlContent
|
||||
import com.fsck.k9.view.RecipientSelectView
|
||||
import com.nhaarman.mockito_kotlin.anyOrNull
|
||||
import org.apache.commons.io.Charsets
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.apache.james.mime4j.util.MimeUtil
|
||||
import org.junit.Assert
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito.*
|
||||
import org.openintents.openpgp.OpenPgpApiManager.OpenPgpProviderState
|
||||
import org.openintents.openpgp.OpenPgpError
|
||||
import org.openintents.openpgp.util.OpenPgpApi
|
||||
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.util.*
|
||||
|
||||
|
||||
class PgpMessageBuilderTest : RobolectricTest() {
|
||||
|
||||
|
||||
private val defaultCryptoStatus = ComposeCryptoStatus(
|
||||
OpenPgpProviderState.OK,
|
||||
TEST_KEY_ID,
|
||||
emptyList<RecipientSelectView.Recipient>(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
CryptoMode.NO_CHOICE
|
||||
)
|
||||
private val openPgpApi = mock(OpenPgpApi::class.java)
|
||||
private val autocryptOpenPgpApiInteractor = mock(AutocryptOpenPgpApiInteractor::class.java)
|
||||
private val pgpMessageBuilder = createDefaultPgpMessageBuilder(openPgpApi, autocryptOpenPgpApiInteractor)
|
||||
|
||||
@Before
|
||||
@Throws(Exception::class)
|
||||
fun setUp() {
|
||||
BinaryTempFileBody.setTempDirectory(RuntimeEnvironment.application.cacheDir)
|
||||
`when`(autocryptOpenPgpApiInteractor.getKeyMaterialForKeyId(openPgpApi, TEST_KEY_ID, SENDER_EMAIL))
|
||||
.thenReturn(AUTOCRYPT_KEY_MATERIAL)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun build__withCryptoProviderUnconfigured__shouldThrow() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(openPgpProviderState = OpenPgpProviderState.UNCONFIGURED)
|
||||
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any<MessagingException>())
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun build__withCryptoProviderUninitialized__shouldThrow() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(openPgpProviderState = OpenPgpProviderState.UNINITIALIZED)
|
||||
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any<MessagingException>())
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun build__withCryptoProviderError__shouldThrow() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(openPgpProviderState = OpenPgpProviderState.ERROR)
|
||||
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any<MessagingException>())
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun buildCleartext__withNoSigningKey__shouldBuildTrivialMessage() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(openPgpKeyId = null)
|
||||
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
val captor = ArgumentCaptor.forClass(MimeMessage::class.java)
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false))
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
|
||||
val message = captor.value
|
||||
assertEquals("text/plain", message.mimeType)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun buildCleartext__shouldSucceed() {
|
||||
pgpMessageBuilder.setCryptoStatus(defaultCryptoStatus)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
val captor = ArgumentCaptor.forClass(MimeMessage::class.java)
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false))
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
|
||||
val message = captor.value
|
||||
assertMessageHasAutocryptHeader(message, SENDER_EMAIL, false, AUTOCRYPT_KEY_MATERIAL)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildSign__withNoDetachedSignatureInResult__shouldThrow() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(cryptoMode = CryptoMode.SIGN_ONLY)
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val returnIntent = Intent()
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
|
||||
`when`(openPgpApi.executeApi(any<Intent>(), any<OpenPgpDataSource>(), any<OutputStream>())).thenReturn(returnIntent)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any<MessagingException>())
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildSign__withDetachedSignatureInResult__shouldSucceed() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(cryptoMode = CryptoMode.SIGN_ONLY)
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val capturedApiIntent = ArgumentCaptor.forClass(Intent::class.java)
|
||||
|
||||
val returnIntent = Intent()
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_DETACHED_SIGNATURE, byteArrayOf(1, 2, 3))
|
||||
`when`(openPgpApi.executeApi(capturedApiIntent.capture(),
|
||||
any<OpenPgpDataSource>(), anyOrNull())).thenReturn(returnIntent)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
val expectedIntent = Intent(OpenPgpApi.ACTION_DETACHED_SIGN)
|
||||
expectedIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, TEST_KEY_ID)
|
||||
expectedIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true)
|
||||
assertIntentEqualsActionAndExtras(expectedIntent, capturedApiIntent.value)
|
||||
|
||||
val captor = ArgumentCaptor.forClass(MimeMessage::class.java)
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false))
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
|
||||
val message = captor.value
|
||||
Assert.assertEquals("message must be multipart/signed", "multipart/signed", message.mimeType)
|
||||
|
||||
val multipart = message.body as MimeMultipart
|
||||
Assert.assertEquals("multipart/signed must consist of two parts", 2, multipart.count.toLong())
|
||||
|
||||
val contentBodyPart = multipart.getBodyPart(0)
|
||||
Assert.assertEquals("first part must have content type text/plain",
|
||||
"text/plain", MimeUtility.getHeaderParameter(contentBodyPart.contentType, null))
|
||||
assertTrue("signed message body must be TextBody", contentBodyPart.body is TextBody)
|
||||
Assert.assertEquals(MimeUtil.ENC_QUOTED_PRINTABLE, (contentBodyPart.body as TextBody).encoding)
|
||||
assertContentOfBodyPartEquals("content must match the message text", contentBodyPart, TEST_MESSAGE_TEXT)
|
||||
|
||||
val signatureBodyPart = multipart.getBodyPart(1)
|
||||
val contentType = signatureBodyPart.contentType
|
||||
Assert.assertEquals("second part must be pgp signature", "application/pgp-signature",
|
||||
MimeUtility.getHeaderParameter(contentType, null))
|
||||
Assert.assertEquals("second part must be called signature.asc", "signature.asc",
|
||||
MimeUtility.getHeaderParameter(contentType, "name"))
|
||||
assertContentOfBodyPartEquals("content must match the supplied detached signature",
|
||||
signatureBodyPart, byteArrayOf(1, 2, 3))
|
||||
|
||||
assertMessageHasAutocryptHeader(message, SENDER_EMAIL, false, AUTOCRYPT_KEY_MATERIAL)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildSign__withUserInteractionResult__shouldReturnUserInteraction() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(cryptoMode = CryptoMode.SIGN_ONLY)
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val returnIntent = mock(Intent::class.java)
|
||||
`when`(returnIntent.getIntExtra(eq(OpenPgpApi.RESULT_CODE), anyInt()))
|
||||
.thenReturn(OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED)
|
||||
val mockPendingIntent = mock(PendingIntent::class.java)
|
||||
`when`<Parcelable>(returnIntent.getParcelableExtra<Parcelable>(eq(OpenPgpApi.RESULT_INTENT)))
|
||||
.thenReturn(mockPendingIntent)
|
||||
|
||||
`when`(openPgpApi.executeApi(any<Intent>(), any<OpenPgpDataSource>(), any<OutputStream>())).thenReturn(returnIntent)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
val captor = ArgumentCaptor.forClass(PendingIntent::class.java)
|
||||
verify(mockCallback).onMessageBuildReturnPendingIntent(captor.capture(), anyInt())
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
|
||||
val pendingIntent = captor.value
|
||||
Assert.assertSame(pendingIntent, mockPendingIntent)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildSign__withReturnAfterUserInteraction__shouldSucceed() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(cryptoMode = CryptoMode.SIGN_ONLY)
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
var returnedRequestCode = 0
|
||||
run {
|
||||
val returnIntent = spy(Intent())
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED)
|
||||
|
||||
val mockPendingIntent = mock(PendingIntent::class.java)
|
||||
`when`<Parcelable>(returnIntent.getParcelableExtra<Parcelable>(eq(OpenPgpApi.RESULT_INTENT)))
|
||||
.thenReturn(mockPendingIntent)
|
||||
|
||||
`when`(openPgpApi.executeApi(any<Intent>(), any<OpenPgpDataSource>(), any<OutputStream>())).thenReturn(returnIntent)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
verify(returnIntent).getIntExtra(eq(OpenPgpApi.RESULT_CODE), anyInt())
|
||||
val piCaptor = ArgumentCaptor.forClass(PendingIntent::class.java)
|
||||
val rcCaptor = ArgumentCaptor.forClass(Int::class.java)
|
||||
verify(mockCallback).onMessageBuildReturnPendingIntent(piCaptor.capture(), rcCaptor.capture())
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
|
||||
returnedRequestCode = rcCaptor.value
|
||||
Assert.assertSame(mockPendingIntent, piCaptor.value)
|
||||
}
|
||||
|
||||
run {
|
||||
val returnIntent = spy(Intent())
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
|
||||
|
||||
val mockReturnIntent = mock(Intent::class.java)
|
||||
`when`(openPgpApi.executeApi(any<Intent>(), any<OpenPgpDataSource>(), any<OutputStream>())).thenReturn(returnIntent)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.onActivityResult(returnedRequestCode, Activity.RESULT_OK, mockReturnIntent, mockCallback)
|
||||
verify(openPgpApi).executeApi(same(mockReturnIntent), any<OpenPgpDataSource>(), any<OutputStream>())
|
||||
verify(returnIntent).getIntExtra(eq(OpenPgpApi.RESULT_CODE), anyInt())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildEncrypt__withoutRecipients__shouldThrow() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(cryptoMode = CryptoMode.CHOICE_ENABLED)
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val returnIntent = spy(Intent())
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
|
||||
`when`(openPgpApi.executeApi(any(Intent::class.java), any(OpenPgpDataSource::class.java), any(OutputStream::class.java)))
|
||||
.thenReturn(returnIntent)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any<MessagingException>())
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildEncrypt__checkGossip() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(cryptoMode = CryptoMode.CHOICE_ENABLED,
|
||||
recipientAddresses = listOf("alice@example.org", "bob@example.org"))
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val returnIntent = Intent()
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
|
||||
`when`(openPgpApi.executeApi(any(Intent::class.java), any(OpenPgpDataSource::class.java), any(OutputStream::class.java)))
|
||||
.thenReturn(returnIntent)
|
||||
pgpMessageBuilder.buildAsync(mock(Callback::class.java))
|
||||
|
||||
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForUserId(same(openPgpApi), eq("alice@example.org"))
|
||||
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForUserId(same(openPgpApi), eq("bob@example.org"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildEncrypt__checkGossip__filterBcc() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(
|
||||
cryptoMode = CryptoMode.CHOICE_ENABLED,
|
||||
recipientAddresses = listOf("alice@example.org", "bob@example.org", "carol@example.org"))
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
pgpMessageBuilder.setBcc(listOf(Address("carol@example.org")))
|
||||
|
||||
val returnIntent = Intent()
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
|
||||
`when`(openPgpApi.executeApi(any(Intent::class.java), any(OpenPgpDataSource::class.java), any(OutputStream::class.java)))
|
||||
.thenReturn(returnIntent)
|
||||
pgpMessageBuilder.buildAsync(mock(Callback::class.java))
|
||||
|
||||
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForKeyId(same(openPgpApi), eq(TEST_KEY_ID), eq(SENDER_EMAIL))
|
||||
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForUserId(same(openPgpApi), eq("alice@example.org"))
|
||||
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForUserId(same(openPgpApi), eq("bob@example.org"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildEncrypt__checkGossip__filterBccSingleRecipient() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(
|
||||
cryptoMode = CryptoMode.CHOICE_ENABLED,
|
||||
isPgpInlineModeEnabled = true,
|
||||
recipientAddresses = listOf("alice@example.org", "carol@example.org"))
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
pgpMessageBuilder.setBcc(listOf(Address("carol@example.org")))
|
||||
|
||||
val returnIntent = Intent()
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
|
||||
`when`(openPgpApi.executeApi(any(Intent::class.java), any(OpenPgpDataSource::class.java), any(OutputStream::class.java)))
|
||||
.thenReturn(returnIntent)
|
||||
pgpMessageBuilder.buildAsync(mock(Callback::class.java))
|
||||
|
||||
verify(autocryptOpenPgpApiInteractor).getKeyMaterialForKeyId(any(OpenPgpApi::class.java), any(Long::class.java), any(String::class.java))
|
||||
verifyNoMoreInteractions(autocryptOpenPgpApiInteractor)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildEncrypt__shouldSucceed() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(
|
||||
cryptoMode = CryptoMode.CHOICE_ENABLED,
|
||||
recipientAddresses = listOf("test@example.org"))
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val capturedApiIntent = ArgumentCaptor.forClass(Intent::class.java)
|
||||
|
||||
val returnIntent = Intent()
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
|
||||
|
||||
`when`(openPgpApi.executeApi(capturedApiIntent.capture(), any(OpenPgpDataSource::class.java),
|
||||
any(OutputStream::class.java))).thenReturn(returnIntent)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
val expectedApiIntent = Intent(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT)
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, TEST_KEY_ID)
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, longArrayOf(TEST_KEY_ID))
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true)
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_USER_IDS, cryptoStatus.recipientAddressesAsArray)
|
||||
assertIntentEqualsActionAndExtras(expectedApiIntent, capturedApiIntent.value)
|
||||
|
||||
val captor = ArgumentCaptor.forClass(MimeMessage::class.java)
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false))
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
|
||||
val message = captor.value
|
||||
|
||||
Assert.assertEquals("message must be multipart/encrypted", "multipart/encrypted", message.mimeType)
|
||||
|
||||
val multipart = message.body as MimeMultipart
|
||||
Assert.assertEquals("multipart/encrypted must consist of two parts", 2, multipart.count.toLong())
|
||||
|
||||
val dummyBodyPart = multipart.getBodyPart(0)
|
||||
Assert.assertEquals("first part must be pgp encrypted dummy part",
|
||||
"application/pgp-encrypted", dummyBodyPart.contentType)
|
||||
assertContentOfBodyPartEquals("content must match the supplied detached signature",
|
||||
dummyBodyPart, "Version: 1")
|
||||
|
||||
val encryptedBodyPart = multipart.getBodyPart(1)
|
||||
Assert.assertEquals("second part must be octet-stream of encrypted data",
|
||||
"application/octet-stream; name=\"encrypted.asc\"", encryptedBodyPart.contentType)
|
||||
assertTrue("message body must be BinaryTempFileBody",
|
||||
encryptedBodyPart.body is BinaryTempFileBody)
|
||||
Assert.assertEquals(MimeUtil.ENC_7BIT, (encryptedBodyPart.body as BinaryTempFileBody).encoding)
|
||||
|
||||
assertMessageHasAutocryptHeader(message, SENDER_EMAIL, false, AUTOCRYPT_KEY_MATERIAL)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildEncrypt__withInlineEnabled__shouldSucceed() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(
|
||||
cryptoMode = CryptoMode.CHOICE_ENABLED,
|
||||
isPgpInlineModeEnabled = true,
|
||||
recipientAddresses = listOf("test@example.org"))
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val capturedApiIntent = ArgumentCaptor.forClass(Intent::class.java)
|
||||
|
||||
val returnIntent = Intent()
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
|
||||
|
||||
`when`(openPgpApi.executeApi(capturedApiIntent.capture(), any(OpenPgpDataSource::class.java),
|
||||
any(OutputStream::class.java))).thenReturn(returnIntent)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
val expectedApiIntent = Intent(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT)
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, TEST_KEY_ID)
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, longArrayOf(TEST_KEY_ID))
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true)
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_USER_IDS, cryptoStatus.recipientAddressesAsArray)
|
||||
assertIntentEqualsActionAndExtras(expectedApiIntent, capturedApiIntent.value)
|
||||
|
||||
val captor = ArgumentCaptor.forClass(MimeMessage::class.java)
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false))
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
|
||||
val message = captor.value
|
||||
Assert.assertEquals("text/plain", message.mimeType)
|
||||
assertTrue("message body must be BinaryTempFileBody", message.body is BinaryTempFileBody)
|
||||
Assert.assertEquals(MimeUtil.ENC_7BIT, (message.body as BinaryTempFileBody).encoding)
|
||||
|
||||
assertMessageHasAutocryptHeader(message, SENDER_EMAIL, false, AUTOCRYPT_KEY_MATERIAL)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildSign__withInlineEnabled__shouldSucceed() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(
|
||||
cryptoMode = CryptoMode.SIGN_ONLY,
|
||||
isPgpInlineModeEnabled = true,
|
||||
recipientAddresses = listOf("test@example.org"))
|
||||
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val capturedApiIntent = ArgumentCaptor.forClass(Intent::class.java)
|
||||
|
||||
val returnIntent = Intent()
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
|
||||
|
||||
`when`(openPgpApi.executeApi(capturedApiIntent.capture(), any(OpenPgpDataSource::class.java),
|
||||
any(OutputStream::class.java))).thenReturn(returnIntent)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
val expectedApiIntent = Intent(OpenPgpApi.ACTION_SIGN)
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, TEST_KEY_ID)
|
||||
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true)
|
||||
assertIntentEqualsActionAndExtras(expectedApiIntent, capturedApiIntent.value)
|
||||
|
||||
val captor = ArgumentCaptor.forClass(MimeMessage::class.java)
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false))
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
|
||||
val message = captor.value
|
||||
Assert.assertEquals("message must be text/plain", "text/plain", message.mimeType)
|
||||
|
||||
assertMessageHasAutocryptHeader(message, SENDER_EMAIL, false, AUTOCRYPT_KEY_MATERIAL)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildSignWithAttach__withInlineEnabled__shouldThrow() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(cryptoMode = CryptoMode.SIGN_ONLY, isPgpInlineModeEnabled = true)
|
||||
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
pgpMessageBuilder.setAttachments(listOf(Attachment.createAttachment(null, 0, null, true)))
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any<MessagingException>())
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
verifyNoMoreInteractions(openPgpApi)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildEncryptWithAttach__withInlineEnabled__shouldThrow() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(cryptoMode = CryptoMode.CHOICE_ENABLED, isPgpInlineModeEnabled = true)
|
||||
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
pgpMessageBuilder.setAttachments(listOf(Attachment.createAttachment(null, 0, null, true)))
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any<MessagingException>())
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
verifyNoMoreInteractions(openPgpApi)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildOpportunisticEncrypt__withNoKeysAndNoSignOnly__shouldNotBeSigned() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(recipientAddresses = listOf("test@example.org"))
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
|
||||
val returnIntent = Intent()
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)
|
||||
returnIntent.putExtra(OpenPgpApi.RESULT_ERROR,
|
||||
OpenPgpError(OpenPgpError.OPPORTUNISTIC_MISSING_KEYS, "Missing keys"))
|
||||
|
||||
|
||||
`when`(openPgpApi.executeApi(any(Intent::class.java), any(OpenPgpDataSource::class.java), any(OutputStream::class.java)))
|
||||
.thenReturn(returnIntent)
|
||||
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
|
||||
val captor = ArgumentCaptor.forClass(MimeMessage::class.java)
|
||||
verify(mockCallback).onMessageBuildSuccess(captor.capture(), eq(false))
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
|
||||
val message = captor.value
|
||||
Assert.assertEquals("text/plain", message.mimeType)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(MessagingException::class)
|
||||
fun buildSign__withNoDetachedSignatureExtra__shouldFail() {
|
||||
val cryptoStatus = defaultCryptoStatus.copy(cryptoMode = CryptoMode.SIGN_ONLY)
|
||||
pgpMessageBuilder.setCryptoStatus(cryptoStatus)
|
||||
|
||||
val returnIntentSigned = Intent()
|
||||
returnIntentSigned.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS)
|
||||
// no OpenPgpApi.EXTRA_DETACHED_SIGNATURE!
|
||||
|
||||
|
||||
`when`(openPgpApi.executeApi(any<Intent>(), any<OpenPgpDataSource>(), any<OutputStream>())).thenReturn(returnIntentSigned)
|
||||
val mockCallback = mock(Callback::class.java)
|
||||
pgpMessageBuilder.buildAsync(mockCallback)
|
||||
|
||||
|
||||
verify(mockCallback).onMessageBuildException(any<MessagingException>())
|
||||
verifyNoMoreInteractions(mockCallback)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TEST_KEY_ID = 123L
|
||||
private val TEST_MESSAGE_TEXT = "message text with a ☭ CCCP symbol"
|
||||
private val AUTOCRYPT_KEY_MATERIAL = byteArrayOf(1, 2, 3)
|
||||
private val SENDER_EMAIL = "test@example.org"
|
||||
|
||||
private fun createDefaultPgpMessageBuilder(openPgpApi: OpenPgpApi,
|
||||
autocryptOpenPgpApiInteractor: AutocryptOpenPgpApiInteractor): PgpMessageBuilder {
|
||||
val builder = PgpMessageBuilder(
|
||||
RuntimeEnvironment.application, MessageIdGenerator.getInstance(), BoundaryGenerator.getInstance(),
|
||||
AutocryptOperations.getInstance(), autocryptOpenPgpApiInteractor)
|
||||
builder.setOpenPgpApi(openPgpApi)
|
||||
|
||||
val identity = Identity()
|
||||
identity.name = "tester"
|
||||
identity.email = SENDER_EMAIL
|
||||
identity.description = "test identity"
|
||||
identity.signatureUse = false
|
||||
|
||||
builder.setSubject("subject")
|
||||
.setSentDate(Date())
|
||||
.setHideTimeZone(false)
|
||||
.setTo(ArrayList())
|
||||
.setCc(ArrayList())
|
||||
.setBcc(ArrayList())
|
||||
.setInReplyTo("inreplyto")
|
||||
.setReferences("references")
|
||||
.setRequestReadReceipt(false)
|
||||
.setIdentity(identity)
|
||||
.setMessageFormat(SimpleMessageFormat.TEXT)
|
||||
.setText(TEST_MESSAGE_TEXT)
|
||||
.setAttachments(ArrayList())
|
||||
.setSignature("signature")
|
||||
.setQuoteStyle(QuoteStyle.PREFIX)
|
||||
.setQuotedTextMode(QuotedTextMode.NONE)
|
||||
.setQuotedText("quoted text")
|
||||
.setQuotedHtmlContent(InsertableHtmlContent())
|
||||
.setReplyAfterQuote(false)
|
||||
.setSignatureBeforeQuotedText(false)
|
||||
.setIdentityChanged(false)
|
||||
.setSignatureChanged(false)
|
||||
.setCursorPosition(0)
|
||||
.setMessageReference(null).isDraft = false
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
private fun assertContentOfBodyPartEquals(reason: String, signatureBodyPart: BodyPart, expected: ByteArray) {
|
||||
try {
|
||||
val bos = ByteArrayOutputStream()
|
||||
signatureBodyPart.body.writeTo(bos)
|
||||
Assert.assertArrayEquals(reason, expected, bos.toByteArray())
|
||||
} catch (e: IOException) {
|
||||
Assert.fail()
|
||||
} catch (e: MessagingException) {
|
||||
Assert.fail()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun assertContentOfBodyPartEquals(reason: String, signatureBodyPart: BodyPart, expected: String) {
|
||||
try {
|
||||
val bos = ByteArrayOutputStream()
|
||||
val inputStream = MimeUtility.decodeBody(signatureBodyPart.body)
|
||||
IOUtils.copy(inputStream, bos)
|
||||
Assert.assertEquals(reason, expected, String(bos.toByteArray(), Charsets.UTF_8))
|
||||
} catch (e: IOException) {
|
||||
Assert.fail()
|
||||
} catch (e: MessagingException) {
|
||||
Assert.fail()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun assertIntentEqualsActionAndExtras(expected: Intent, actual: Intent) {
|
||||
Assert.assertEquals(expected.action, actual.action)
|
||||
|
||||
val expectedExtras = expected.extras
|
||||
val intentExtras = actual.extras
|
||||
|
||||
if (expectedExtras!!.size() != intentExtras!!.size()) {
|
||||
Assert.assertEquals(expectedExtras.size().toLong(), intentExtras.size().toLong())
|
||||
}
|
||||
|
||||
for (key in expectedExtras.keySet()) {
|
||||
val intentExtra = intentExtras.get(key)
|
||||
val expectedExtra = expectedExtras.get(key)
|
||||
if (intentExtra == null) {
|
||||
if (expectedExtra == null) {
|
||||
continue
|
||||
}
|
||||
Assert.fail("found null for an expected non-null extra: $key")
|
||||
}
|
||||
if (intentExtra is LongArray) {
|
||||
if (!Arrays.equals(intentExtra, expectedExtra as LongArray)) {
|
||||
Assert.assertArrayEquals("error in $key", expectedExtra, intentExtra)
|
||||
}
|
||||
} else {
|
||||
if (intentExtra != expectedExtra) {
|
||||
Assert.assertEquals("error in $key", expectedExtra, intentExtra)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -665,7 +665,8 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||
return null;
|
||||
}
|
||||
|
||||
if (cryptoStatus.shouldUsePgpMessageBuilder()) {
|
||||
boolean shouldUsePgpMessageBuilder = cryptoStatus.isOpenPgpConfigured();
|
||||
if (shouldUsePgpMessageBuilder) {
|
||||
SendErrorState maybeSendErrorState = cryptoStatus.getSendErrorStateOrNull();
|
||||
if (maybeSendErrorState != null) {
|
||||
recipientPresenter.showPgpSendError(maybeSendErrorState);
|
||||
|
|
|
@ -17,11 +17,11 @@ import android.os.Handler;
|
|||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
|
||||
import com.fsck.k9.controller.MessageReference;
|
||||
import com.fsck.k9.activity.compose.ComposeCryptoStatus.AttachErrorState;
|
||||
import com.fsck.k9.activity.loader.AttachmentContentLoader;
|
||||
import com.fsck.k9.activity.loader.AttachmentInfoLoader;
|
||||
import com.fsck.k9.activity.misc.Attachment;
|
||||
import com.fsck.k9.controller.MessageReference;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mailstore.AttachmentViewInfo;
|
||||
import com.fsck.k9.mailstore.LocalMessage;
|
||||
|
|
|
@ -1,322 +0,0 @@
|
|||
package com.fsck.k9.activity.compose;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
|
||||
import com.fsck.k9.activity.compose.RecipientMvpView.CryptoSpecialModeDisplayType;
|
||||
import com.fsck.k9.activity.compose.RecipientMvpView.CryptoStatusDisplayType;
|
||||
import com.fsck.k9.activity.compose.RecipientPresenter.CryptoMode;
|
||||
import com.fsck.k9.message.AutocryptStatusInteractor.RecipientAutocryptStatus;
|
||||
import com.fsck.k9.message.AutocryptStatusInteractor.RecipientAutocryptStatusType;
|
||||
import com.fsck.k9.message.CryptoStatus;
|
||||
import org.openintents.openpgp.OpenPgpApiManager.OpenPgpProviderState;
|
||||
import com.fsck.k9.view.RecipientSelectView.Recipient;
|
||||
|
||||
/** This is an immutable object which contains all relevant metadata entered
|
||||
* during email composition to apply cryptographic operations before sending
|
||||
* or saving as draft.
|
||||
*/
|
||||
public class ComposeCryptoStatus implements CryptoStatus {
|
||||
private OpenPgpProviderState openPgpProviderState;
|
||||
private Long openPgpKeyId;
|
||||
private String[] recipientAddresses;
|
||||
private boolean enablePgpInline;
|
||||
private boolean preferEncryptMutual;
|
||||
private boolean isReplyToEncrypted;
|
||||
private boolean encryptSubject;
|
||||
private CryptoMode cryptoMode;
|
||||
private RecipientAutocryptStatus recipientAutocryptStatus;
|
||||
|
||||
|
||||
public Long getOpenPgpKeyId() {
|
||||
return openPgpKeyId;
|
||||
}
|
||||
|
||||
CryptoStatusDisplayType getCryptoStatusDisplayType() {
|
||||
switch (openPgpProviderState) {
|
||||
case UNCONFIGURED:
|
||||
return CryptoStatusDisplayType.UNCONFIGURED;
|
||||
case UNINITIALIZED:
|
||||
return CryptoStatusDisplayType.UNINITIALIZED;
|
||||
case ERROR:
|
||||
case UI_REQUIRED:
|
||||
return CryptoStatusDisplayType.ERROR;
|
||||
case OK:
|
||||
// provider status is ok -> return value is based on cryptoMode
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("all CryptoProviderStates must be handled!");
|
||||
}
|
||||
|
||||
if (recipientAutocryptStatus == null) {
|
||||
throw new IllegalStateException("Display type must be obtained from provider!");
|
||||
}
|
||||
|
||||
RecipientAutocryptStatusType recipientAutocryptStatusType = recipientAutocryptStatus.type;
|
||||
|
||||
if (recipientAutocryptStatusType == RecipientAutocryptStatusType.ERROR) {
|
||||
return CryptoStatusDisplayType.ERROR;
|
||||
}
|
||||
|
||||
if (isEncryptionEnabled()) {
|
||||
if (!recipientAutocryptStatusType.canEncrypt()) {
|
||||
return CryptoStatusDisplayType.ENABLED_ERROR;
|
||||
} else if (recipientAutocryptStatusType.isConfirmed()) {
|
||||
return CryptoStatusDisplayType.ENABLED_TRUSTED;
|
||||
} else {
|
||||
return CryptoStatusDisplayType.ENABLED;
|
||||
}
|
||||
} else if (isSigningEnabled()) {
|
||||
return CryptoStatusDisplayType.SIGN_ONLY;
|
||||
} else if (recipientAutocryptStatusType.canEncrypt()) {
|
||||
return CryptoStatusDisplayType.AVAILABLE;
|
||||
} else {
|
||||
return CryptoStatusDisplayType.UNAVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
CryptoSpecialModeDisplayType getCryptoSpecialModeDisplayType() {
|
||||
if (openPgpProviderState != OpenPgpProviderState.OK) {
|
||||
return CryptoSpecialModeDisplayType.NONE;
|
||||
}
|
||||
|
||||
if (isSignOnly() && isPgpInlineModeEnabled()) {
|
||||
return CryptoSpecialModeDisplayType.SIGN_ONLY_PGP_INLINE;
|
||||
}
|
||||
|
||||
if (isSignOnly()) {
|
||||
return CryptoSpecialModeDisplayType.SIGN_ONLY;
|
||||
}
|
||||
|
||||
if (allRecipientsCanEncrypt() && isPgpInlineModeEnabled()) {
|
||||
return CryptoSpecialModeDisplayType.PGP_INLINE;
|
||||
}
|
||||
|
||||
return CryptoSpecialModeDisplayType.NONE;
|
||||
}
|
||||
|
||||
public boolean shouldUsePgpMessageBuilder() {
|
||||
// CryptoProviderState.ERROR will be handled as an actual error, see SendErrorState
|
||||
return openPgpProviderState != OpenPgpProviderState.UNCONFIGURED;
|
||||
}
|
||||
|
||||
public boolean isEncryptionEnabled() {
|
||||
if (openPgpProviderState == OpenPgpProviderState.UNCONFIGURED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isExplicitlyEnabled = (cryptoMode == CryptoMode.CHOICE_ENABLED);
|
||||
boolean isMutualAndNotDisabled = (cryptoMode != CryptoMode.CHOICE_DISABLED && canEncryptAndIsMutualDefault());
|
||||
boolean isReplyAndNotDisabled = (cryptoMode != CryptoMode.CHOICE_DISABLED && isReplyToEncrypted());
|
||||
return isExplicitlyEnabled || isMutualAndNotDisabled || isReplyAndNotDisabled;
|
||||
}
|
||||
|
||||
public boolean isSignOnly() {
|
||||
return cryptoMode == CryptoMode.SIGN_ONLY;
|
||||
}
|
||||
|
||||
public boolean isSigningEnabled() {
|
||||
return cryptoMode == CryptoMode.SIGN_ONLY || isEncryptionEnabled();
|
||||
}
|
||||
|
||||
public boolean isPgpInlineModeEnabled() {
|
||||
return enablePgpInline;
|
||||
}
|
||||
|
||||
public boolean isProviderStateOk() {
|
||||
return openPgpProviderState == OpenPgpProviderState.OK;
|
||||
}
|
||||
|
||||
boolean allRecipientsCanEncrypt() {
|
||||
return recipientAutocryptStatus != null && recipientAutocryptStatus.type.canEncrypt();
|
||||
}
|
||||
|
||||
public boolean isUserChoice() {
|
||||
return cryptoMode != CryptoMode.NO_CHOICE;
|
||||
}
|
||||
|
||||
public String[] getRecipientAddresses() {
|
||||
return recipientAddresses;
|
||||
}
|
||||
|
||||
public boolean hasRecipients() {
|
||||
return recipientAddresses.length > 0;
|
||||
}
|
||||
|
||||
public boolean isSenderPreferEncryptMutual() {
|
||||
return preferEncryptMutual;
|
||||
}
|
||||
|
||||
private boolean isRecipientsPreferEncryptMutual() {
|
||||
return recipientAutocryptStatus.type.isMutual();
|
||||
}
|
||||
|
||||
public boolean isReplyToEncrypted() {
|
||||
return isReplyToEncrypted;
|
||||
}
|
||||
|
||||
boolean canEncryptAndIsMutualDefault() {
|
||||
return allRecipientsCanEncrypt() && isSenderPreferEncryptMutual() && isRecipientsPreferEncryptMutual();
|
||||
}
|
||||
|
||||
boolean hasAutocryptPendingIntent() {
|
||||
return recipientAutocryptStatus.hasPendingIntent();
|
||||
}
|
||||
|
||||
PendingIntent getAutocryptPendingIntent() {
|
||||
return recipientAutocryptStatus.intent;
|
||||
}
|
||||
|
||||
public boolean isEncryptSubject() {
|
||||
return encryptSubject;
|
||||
}
|
||||
|
||||
public static class ComposeCryptoStatusBuilder {
|
||||
|
||||
private OpenPgpProviderState openPgpProviderState;
|
||||
private CryptoMode cryptoMode;
|
||||
private Long openPgpKeyId;
|
||||
private List<Recipient> recipients;
|
||||
private Boolean enablePgpInline;
|
||||
private Boolean preferEncryptMutual;
|
||||
private Boolean isReplyToEncrypted;
|
||||
private Boolean encryptSubject;
|
||||
|
||||
public ComposeCryptoStatusBuilder setOpenPgpProviderState(OpenPgpProviderState openPgpProviderState) {
|
||||
this.openPgpProviderState = openPgpProviderState;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComposeCryptoStatusBuilder setCryptoMode(CryptoMode cryptoMode) {
|
||||
this.cryptoMode = cryptoMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComposeCryptoStatusBuilder setOpenPgpKeyId(Long openPgpKeyId) {
|
||||
this.openPgpKeyId = openPgpKeyId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComposeCryptoStatusBuilder setRecipients(List<Recipient> recipients) {
|
||||
this.recipients = recipients;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComposeCryptoStatusBuilder setEnablePgpInline(boolean cryptoEnableCompat) {
|
||||
this.enablePgpInline = cryptoEnableCompat;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComposeCryptoStatusBuilder setPreferEncryptMutual(boolean preferEncryptMutual) {
|
||||
this.preferEncryptMutual = preferEncryptMutual;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComposeCryptoStatusBuilder setIsReplyToEncrypted(boolean isReplyToEncrypted) {
|
||||
this.isReplyToEncrypted = isReplyToEncrypted;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComposeCryptoStatusBuilder setEncryptSubject(boolean encryptSubject) {
|
||||
this.encryptSubject = encryptSubject;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComposeCryptoStatus build() {
|
||||
if (openPgpProviderState == null) {
|
||||
throw new AssertionError("cryptoProviderState must be set!");
|
||||
}
|
||||
if (cryptoMode == null) {
|
||||
throw new AssertionError("crypto mode must be set!");
|
||||
}
|
||||
if (recipients == null) {
|
||||
throw new AssertionError("recipients must be set!");
|
||||
}
|
||||
if (enablePgpInline == null) {
|
||||
throw new AssertionError("enablePgpInline must be set!");
|
||||
}
|
||||
if (preferEncryptMutual == null) {
|
||||
throw new AssertionError("preferEncryptMutual must be set!");
|
||||
}
|
||||
if (isReplyToEncrypted == null) {
|
||||
throw new AssertionError("isReplyToEncrypted must be set!");
|
||||
}
|
||||
if (encryptSubject == null) {
|
||||
throw new AssertionError("encryptSubject must be set!");
|
||||
}
|
||||
|
||||
ArrayList<String> recipientAddresses = new ArrayList<>();
|
||||
for (Recipient recipient : recipients) {
|
||||
recipientAddresses.add(recipient.address.getAddress());
|
||||
}
|
||||
|
||||
ComposeCryptoStatus result = new ComposeCryptoStatus();
|
||||
result.openPgpProviderState = openPgpProviderState;
|
||||
result.cryptoMode = cryptoMode;
|
||||
result.recipientAddresses = recipientAddresses.toArray(new String[0]);
|
||||
result.openPgpKeyId = openPgpKeyId;
|
||||
result.isReplyToEncrypted = isReplyToEncrypted;
|
||||
result.enablePgpInline = enablePgpInline;
|
||||
result.preferEncryptMutual = preferEncryptMutual;
|
||||
result.encryptSubject = encryptSubject;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
ComposeCryptoStatus withRecipientAutocryptStatus(RecipientAutocryptStatus recipientAutocryptStatusType) {
|
||||
ComposeCryptoStatus result = new ComposeCryptoStatus();
|
||||
result.openPgpProviderState = openPgpProviderState;
|
||||
result.cryptoMode = cryptoMode;
|
||||
result.recipientAddresses = recipientAddresses;
|
||||
result.isReplyToEncrypted = isReplyToEncrypted;
|
||||
result.openPgpKeyId = openPgpKeyId;
|
||||
result.enablePgpInline = enablePgpInline;
|
||||
result.preferEncryptMutual = preferEncryptMutual;
|
||||
result.encryptSubject = encryptSubject;
|
||||
result.recipientAutocryptStatus = recipientAutocryptStatusType;
|
||||
return result;
|
||||
}
|
||||
|
||||
public enum SendErrorState {
|
||||
PROVIDER_ERROR,
|
||||
KEY_CONFIG_ERROR,
|
||||
ENABLED_ERROR
|
||||
}
|
||||
|
||||
public SendErrorState getSendErrorStateOrNull() {
|
||||
if (openPgpProviderState != OpenPgpProviderState.OK) {
|
||||
// TODO: be more specific about this error
|
||||
return SendErrorState.PROVIDER_ERROR;
|
||||
}
|
||||
|
||||
if (openPgpKeyId == null && (isEncryptionEnabled() || isSignOnly())) {
|
||||
return SendErrorState.KEY_CONFIG_ERROR;
|
||||
}
|
||||
|
||||
if (isEncryptionEnabled() && !allRecipientsCanEncrypt()) {
|
||||
return SendErrorState.ENABLED_ERROR;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
enum AttachErrorState {
|
||||
IS_INLINE
|
||||
}
|
||||
|
||||
AttachErrorState getAttachErrorStateOrNull() {
|
||||
if (openPgpProviderState == OpenPgpProviderState.UNCONFIGURED) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (enablePgpInline) {
|
||||
return AttachErrorState.IS_INLINE;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
package com.fsck.k9.activity.compose
|
||||
|
||||
|
||||
import com.fsck.k9.activity.compose.RecipientMvpView.CryptoSpecialModeDisplayType
|
||||
import com.fsck.k9.activity.compose.RecipientMvpView.CryptoStatusDisplayType
|
||||
import com.fsck.k9.activity.compose.RecipientPresenter.CryptoMode
|
||||
import com.fsck.k9.message.AutocryptStatusInteractor
|
||||
import com.fsck.k9.message.AutocryptStatusInteractor.RecipientAutocryptStatus
|
||||
import com.fsck.k9.message.CryptoStatus
|
||||
import com.fsck.k9.view.RecipientSelectView.Recipient
|
||||
import org.openintents.openpgp.OpenPgpApiManager
|
||||
import org.openintents.openpgp.OpenPgpApiManager.OpenPgpProviderState
|
||||
|
||||
/** This is an immutable object which contains all relevant metadata entered
|
||||
* during email composition to apply cryptographic operations before sending
|
||||
* or saving as draft.
|
||||
*/
|
||||
data class ComposeCryptoStatus(private val openPgpProviderState: OpenPgpProviderState,
|
||||
private val cryptoMode: CryptoMode,
|
||||
override val openPgpKeyId: Long?,
|
||||
override val isPgpInlineModeEnabled: Boolean,
|
||||
override val isSenderPreferEncryptMutual: Boolean,
|
||||
override val isReplyToEncrypted: Boolean,
|
||||
override val isEncryptSubject: Boolean,
|
||||
val recipientAddresses: List<String>,
|
||||
private val recipientAutocryptStatus: RecipientAutocryptStatus? = null) : CryptoStatus {
|
||||
|
||||
constructor(openPgpProviderState: OpenPgpProviderState,
|
||||
openPgpKeyId: Long?,
|
||||
recipientAddresses: List<Recipient>,
|
||||
isPgpInlineModeEnabled: Boolean,
|
||||
isSenderPreferEncryptMutual: Boolean,
|
||||
isReplyToEncrypted: Boolean,
|
||||
isEncryptSubject: Boolean,
|
||||
cryptoMode: CryptoMode) : this(
|
||||
openPgpProviderState, cryptoMode,
|
||||
openPgpKeyId,
|
||||
isPgpInlineModeEnabled, isSenderPreferEncryptMutual, isReplyToEncrypted, isEncryptSubject, recipientAddresses.map { it.address.address })
|
||||
|
||||
private val recipientAutocryptStatusType = recipientAutocryptStatus?.type
|
||||
private val isRecipientsPreferEncryptMutual = recipientAutocryptStatus?.type?.isMutual ?: false
|
||||
|
||||
private val isExplicitlyEnabled = cryptoMode == CryptoMode.CHOICE_ENABLED
|
||||
private val isMutualAndNotDisabled = cryptoMode != CryptoMode.CHOICE_DISABLED && canEncryptAndIsMutualDefault()
|
||||
private val isReplyAndNotDisabled = cryptoMode != CryptoMode.CHOICE_DISABLED && isReplyToEncrypted
|
||||
|
||||
val isOpenPgpConfigured = openPgpProviderState != OpenPgpProviderState.UNCONFIGURED
|
||||
|
||||
override val isSignOnly = cryptoMode == CryptoMode.SIGN_ONLY
|
||||
|
||||
override val isEncryptionEnabled = when {
|
||||
openPgpProviderState == OpenPgpProviderState.UNCONFIGURED -> false
|
||||
isSignOnly -> false
|
||||
isExplicitlyEnabled -> true
|
||||
isMutualAndNotDisabled -> true
|
||||
isReplyAndNotDisabled -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun isProviderStateOk() = openPgpProviderState == OpenPgpProviderState.OK
|
||||
|
||||
override fun isUserChoice() = cryptoMode != CryptoMode.NO_CHOICE
|
||||
override fun isSigningEnabled() = cryptoMode == CryptoMode.SIGN_ONLY || isEncryptionEnabled
|
||||
val recipientAddressesAsArray = recipientAddresses.toTypedArray()
|
||||
|
||||
private val displayTypeFromProviderError = when (openPgpProviderState) {
|
||||
OpenPgpApiManager.OpenPgpProviderState.OK -> null
|
||||
OpenPgpApiManager.OpenPgpProviderState.UNCONFIGURED -> CryptoStatusDisplayType.UNCONFIGURED
|
||||
OpenPgpApiManager.OpenPgpProviderState.UNINITIALIZED -> CryptoStatusDisplayType.UNINITIALIZED
|
||||
OpenPgpApiManager.OpenPgpProviderState.ERROR, OpenPgpApiManager.OpenPgpProviderState.UI_REQUIRED -> CryptoStatusDisplayType.ERROR
|
||||
}
|
||||
|
||||
private val displayTypeFromAutocryptError = when (recipientAutocryptStatusType) {
|
||||
null, AutocryptStatusInteractor.RecipientAutocryptStatusType.ERROR -> CryptoStatusDisplayType.ERROR
|
||||
else -> null
|
||||
}
|
||||
|
||||
private val displayTypeFromEnabledAutocryptStatus = when {
|
||||
!isEncryptionEnabled -> null
|
||||
recipientAutocryptStatusType == null -> CryptoStatusDisplayType.ERROR
|
||||
!recipientAutocryptStatusType.canEncrypt() -> CryptoStatusDisplayType.ENABLED_ERROR
|
||||
recipientAutocryptStatusType.isConfirmed -> CryptoStatusDisplayType.ENABLED_TRUSTED
|
||||
else -> CryptoStatusDisplayType.ENABLED
|
||||
}
|
||||
|
||||
private val displayTypeFromSignOnly = when {
|
||||
isSignOnly -> CryptoStatusDisplayType.SIGN_ONLY
|
||||
else -> null
|
||||
}
|
||||
|
||||
private val displayTypeFromEncryptionAvailable = when {
|
||||
recipientAutocryptStatusType?.canEncrypt() == true -> CryptoStatusDisplayType.AVAILABLE
|
||||
else -> null
|
||||
}
|
||||
|
||||
val displayType =
|
||||
displayTypeFromProviderError
|
||||
?: displayTypeFromAutocryptError
|
||||
?: displayTypeFromEnabledAutocryptStatus
|
||||
?: displayTypeFromSignOnly
|
||||
?: displayTypeFromEncryptionAvailable
|
||||
?: CryptoStatusDisplayType.UNAVAILABLE
|
||||
|
||||
val specialModeDisplayType = when {
|
||||
openPgpProviderState != OpenPgpProviderState.OK -> CryptoSpecialModeDisplayType.NONE
|
||||
isSignOnly && isPgpInlineModeEnabled -> CryptoSpecialModeDisplayType.SIGN_ONLY_PGP_INLINE
|
||||
isSignOnly -> CryptoSpecialModeDisplayType.SIGN_ONLY
|
||||
allRecipientsCanEncrypt() && isPgpInlineModeEnabled -> CryptoSpecialModeDisplayType.PGP_INLINE
|
||||
else -> CryptoSpecialModeDisplayType.NONE
|
||||
}
|
||||
|
||||
val autocryptPendingIntent = recipientAutocryptStatus?.intent
|
||||
|
||||
val sendErrorStateOrNull = when {
|
||||
openPgpProviderState != OpenPgpProviderState.OK -> SendErrorState.PROVIDER_ERROR
|
||||
openPgpKeyId == null && (isEncryptionEnabled || isSignOnly) -> SendErrorState.KEY_CONFIG_ERROR
|
||||
isEncryptionEnabled && !allRecipientsCanEncrypt() -> SendErrorState.ENABLED_ERROR
|
||||
else -> null
|
||||
}
|
||||
|
||||
val attachErrorStateOrNull = when {
|
||||
openPgpProviderState == OpenPgpProviderState.UNCONFIGURED -> null
|
||||
isPgpInlineModeEnabled -> AttachErrorState.IS_INLINE
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun allRecipientsCanEncrypt() = recipientAutocryptStatus?.type?.canEncrypt() == true
|
||||
|
||||
fun canEncryptAndIsMutualDefault() = allRecipientsCanEncrypt() && isSenderPreferEncryptMutual && isRecipientsPreferEncryptMutual
|
||||
|
||||
fun hasAutocryptPendingIntent() = recipientAutocryptStatus?.hasPendingIntent() == true
|
||||
|
||||
override fun hasRecipients(): Boolean {
|
||||
return recipientAddresses.isNotEmpty()
|
||||
}
|
||||
|
||||
override fun getRecipientAddresses() = recipientAddresses.toTypedArray()
|
||||
|
||||
fun withRecipientAutocryptStatus(recipientAutocryptStatusType: RecipientAutocryptStatus) = ComposeCryptoStatus(
|
||||
openPgpProviderState = openPgpProviderState,
|
||||
cryptoMode = cryptoMode,
|
||||
openPgpKeyId = openPgpKeyId,
|
||||
isPgpInlineModeEnabled = isPgpInlineModeEnabled,
|
||||
isSenderPreferEncryptMutual = isSenderPreferEncryptMutual,
|
||||
isReplyToEncrypted = isReplyToEncrypted,
|
||||
isEncryptSubject = isEncryptSubject,
|
||||
recipientAddresses = recipientAddresses,
|
||||
recipientAutocryptStatus = recipientAutocryptStatusType
|
||||
)
|
||||
|
||||
enum class SendErrorState {
|
||||
PROVIDER_ERROR,
|
||||
KEY_CONFIG_ERROR,
|
||||
ENABLED_ERROR
|
||||
}
|
||||
|
||||
enum class AttachErrorState {
|
||||
IS_INLINE
|
||||
}
|
||||
|
||||
}
|
|
@ -22,9 +22,7 @@ import android.view.Menu;
|
|||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.Identity;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.ui.R;
|
||||
import com.fsck.k9.activity.compose.ComposeCryptoStatus.AttachErrorState;
|
||||
import com.fsck.k9.activity.compose.ComposeCryptoStatus.ComposeCryptoStatusBuilder;
|
||||
import com.fsck.k9.activity.compose.ComposeCryptoStatus.SendErrorState;
|
||||
import com.fsck.k9.activity.compose.RecipientMvpView.CryptoStatusDisplayType;
|
||||
import com.fsck.k9.autocrypt.AutocryptDraftStateHeader;
|
||||
|
@ -44,6 +42,7 @@ import com.fsck.k9.message.ComposePgpEnableByDefaultDecider;
|
|||
import com.fsck.k9.message.ComposePgpInlineDecider;
|
||||
import com.fsck.k9.message.MessageBuilder;
|
||||
import com.fsck.k9.message.PgpMessageBuilder;
|
||||
import com.fsck.k9.ui.R;
|
||||
import com.fsck.k9.view.RecipientSelectView.Recipient;
|
||||
import org.openintents.openpgp.OpenPgpApiManager;
|
||||
import org.openintents.openpgp.OpenPgpApiManager.OpenPgpApiManagerCallback;
|
||||
|
@ -423,16 +422,15 @@ public class RecipientPresenter {
|
|||
accountCryptoKey = null;
|
||||
}
|
||||
|
||||
final ComposeCryptoStatus composeCryptoStatus = new ComposeCryptoStatusBuilder()
|
||||
.setOpenPgpProviderState(openPgpProviderState)
|
||||
.setCryptoMode(currentCryptoMode)
|
||||
.setEnablePgpInline(cryptoEnablePgpInline)
|
||||
.setPreferEncryptMutual(account.getAutocryptPreferEncryptMutual())
|
||||
.setIsReplyToEncrypted(isReplyToEncryptedMessage)
|
||||
.setEncryptSubject(account.getOpenPgpEncryptSubject())
|
||||
.setRecipients(getAllRecipients())
|
||||
.setOpenPgpKeyId(accountCryptoKey)
|
||||
.build();
|
||||
final ComposeCryptoStatus composeCryptoStatus = new ComposeCryptoStatus(
|
||||
openPgpProviderState,
|
||||
accountCryptoKey,
|
||||
getAllRecipients(),
|
||||
cryptoEnablePgpInline,
|
||||
account.getAutocryptPreferEncryptMutual(),
|
||||
isReplyToEncryptedMessage,
|
||||
account.getOpenPgpEncryptSubject(),
|
||||
currentCryptoMode);
|
||||
|
||||
if (openPgpProviderState != OpenPgpProviderState.OK) {
|
||||
cachedCryptoStatus = composeCryptoStatus;
|
||||
|
@ -440,7 +438,7 @@ public class RecipientPresenter {
|
|||
return;
|
||||
}
|
||||
|
||||
final String[] recipientAddresses = composeCryptoStatus.getRecipientAddresses();
|
||||
final String[] recipientAddresses = composeCryptoStatus.getRecipientAddressesAsArray();
|
||||
|
||||
new AsyncTask<Void,Void,RecipientAutocryptStatus>() {
|
||||
@Override
|
||||
|
@ -472,9 +470,9 @@ public class RecipientPresenter {
|
|||
|
||||
recipientMvpView.setRecipientTokensShowCryptoEnabled(cachedCryptoStatus.isEncryptionEnabled());
|
||||
|
||||
CryptoStatusDisplayType cryptoStatusDisplayType = cachedCryptoStatus.getCryptoStatusDisplayType();
|
||||
CryptoStatusDisplayType cryptoStatusDisplayType = cachedCryptoStatus.getDisplayType();
|
||||
recipientMvpView.showCryptoStatus(cryptoStatusDisplayType);
|
||||
recipientMvpView.showCryptoSpecialMode(cachedCryptoStatus.getCryptoSpecialModeDisplayType());
|
||||
recipientMvpView.showCryptoSpecialMode(cachedCryptoStatus.getSpecialModeDisplayType());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
@ -150,11 +150,11 @@ public class RecipientPresenterTest extends K9RobolectricTest {
|
|||
|
||||
ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus();
|
||||
|
||||
assertEquals(CryptoStatusDisplayType.UNCONFIGURED, status.getCryptoStatusDisplayType());
|
||||
assertEquals(CryptoSpecialModeDisplayType.NONE, status.getCryptoSpecialModeDisplayType());
|
||||
assertEquals(CryptoStatusDisplayType.UNCONFIGURED, status.getDisplayType());
|
||||
assertEquals(CryptoSpecialModeDisplayType.NONE, status.getSpecialModeDisplayType());
|
||||
assertNull(status.getAttachErrorStateOrNull());
|
||||
assertFalse(status.isProviderStateOk());
|
||||
assertFalse(status.shouldUsePgpMessageBuilder());
|
||||
assertFalse(status.isOpenPgpConfigured());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -163,9 +163,9 @@ public class RecipientPresenterTest extends K9RobolectricTest {
|
|||
|
||||
ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus();
|
||||
|
||||
assertEquals(CryptoStatusDisplayType.UNAVAILABLE, status.getCryptoStatusDisplayType());
|
||||
assertEquals(CryptoStatusDisplayType.UNAVAILABLE, status.getDisplayType());
|
||||
assertTrue(status.isProviderStateOk());
|
||||
assertTrue(status.shouldUsePgpMessageBuilder());
|
||||
assertTrue(status.isOpenPgpConfigured());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -176,9 +176,9 @@ public class RecipientPresenterTest extends K9RobolectricTest {
|
|||
|
||||
ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus();
|
||||
|
||||
assertEquals(CryptoStatusDisplayType.AVAILABLE, status.getCryptoStatusDisplayType());
|
||||
assertEquals(CryptoStatusDisplayType.AVAILABLE, status.getDisplayType());
|
||||
assertTrue(status.isProviderStateOk());
|
||||
assertTrue(status.shouldUsePgpMessageBuilder());
|
||||
assertTrue(status.isOpenPgpConfigured());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -189,9 +189,9 @@ public class RecipientPresenterTest extends K9RobolectricTest {
|
|||
|
||||
ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus();
|
||||
|
||||
assertEquals(CryptoStatusDisplayType.AVAILABLE, status.getCryptoStatusDisplayType());
|
||||
assertEquals(CryptoStatusDisplayType.AVAILABLE, status.getDisplayType());
|
||||
assertTrue(status.isProviderStateOk());
|
||||
assertTrue(status.shouldUsePgpMessageBuilder());
|
||||
assertTrue(status.isOpenPgpConfigured());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -202,9 +202,9 @@ public class RecipientPresenterTest extends K9RobolectricTest {
|
|||
|
||||
ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus();
|
||||
|
||||
assertEquals(CryptoStatusDisplayType.UNAVAILABLE, status.getCryptoStatusDisplayType());
|
||||
assertEquals(CryptoStatusDisplayType.UNAVAILABLE, status.getDisplayType());
|
||||
assertTrue(status.isProviderStateOk());
|
||||
assertTrue(status.shouldUsePgpMessageBuilder());
|
||||
assertTrue(status.isOpenPgpConfigured());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -217,9 +217,9 @@ public class RecipientPresenterTest extends K9RobolectricTest {
|
|||
runBackgroundTask();
|
||||
ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus();
|
||||
|
||||
assertEquals(CryptoStatusDisplayType.ENABLED_ERROR, status.getCryptoStatusDisplayType());
|
||||
assertEquals(CryptoStatusDisplayType.ENABLED_ERROR, status.getDisplayType());
|
||||
assertTrue(status.isProviderStateOk());
|
||||
assertTrue(status.shouldUsePgpMessageBuilder());
|
||||
assertTrue(status.isOpenPgpConfigured());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -232,9 +232,9 @@ public class RecipientPresenterTest extends K9RobolectricTest {
|
|||
runBackgroundTask();
|
||||
ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus();
|
||||
|
||||
assertEquals(CryptoStatusDisplayType.AVAILABLE, status.getCryptoStatusDisplayType());
|
||||
assertEquals(CryptoStatusDisplayType.AVAILABLE, status.getDisplayType());
|
||||
assertTrue(status.isProviderStateOk());
|
||||
assertTrue(status.shouldUsePgpMessageBuilder());
|
||||
assertTrue(status.isOpenPgpConfigured());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -247,9 +247,9 @@ public class RecipientPresenterTest extends K9RobolectricTest {
|
|||
runBackgroundTask();
|
||||
ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus();
|
||||
|
||||
assertEquals(CryptoStatusDisplayType.ENABLED, status.getCryptoStatusDisplayType());
|
||||
assertEquals(CryptoStatusDisplayType.ENABLED, status.getDisplayType());
|
||||
assertTrue(status.isProviderStateOk());
|
||||
assertTrue(status.shouldUsePgpMessageBuilder());
|
||||
assertTrue(status.isOpenPgpConfigured());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -260,7 +260,7 @@ public class RecipientPresenterTest extends K9RobolectricTest {
|
|||
runBackgroundTask();
|
||||
ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus();
|
||||
|
||||
assertEquals(CryptoStatusDisplayType.SIGN_ONLY, status.getCryptoStatusDisplayType());
|
||||
assertEquals(CryptoStatusDisplayType.SIGN_ONLY, status.getDisplayType());
|
||||
assertTrue(status.isProviderStateOk());
|
||||
assertTrue(status.isSigningEnabled());
|
||||
assertTrue(status.isSignOnly());
|
||||
|
@ -274,7 +274,7 @@ public class RecipientPresenterTest extends K9RobolectricTest {
|
|||
runBackgroundTask();
|
||||
ComposeCryptoStatus status = recipientPresenter.getCurrentCachedCryptoStatus();
|
||||
|
||||
assertEquals(CryptoStatusDisplayType.UNAVAILABLE, status.getCryptoStatusDisplayType());
|
||||
assertEquals(CryptoStatusDisplayType.UNAVAILABLE, status.getDisplayType());
|
||||
assertTrue(status.isProviderStateOk());
|
||||
assertTrue(status.isPgpInlineModeEnabled());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue