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 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.K9RobolectricTestRunner;
import com.fsck.k9.activity.compose.ComposeCryptoStatus;
import com.fsck.k9.activity.compose.ComposeCryptoStatus.ComposeCryptoStatusBuilder;
import com.fsck.k9.activity.compose.RecipientPresenter.CryptoMode;
import com.fsck.k9.activity.compose.RecipientPresenter.CryptoProviderState;
import com.fsck.k9.activity.misc.Attachment;
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 com.fsck.k9.view.RecipientSelectView.Recipient;
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.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource;
import org.robolectric.RuntimeEnvironment;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
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;
@RunWith(K9RobolectricTestRunner.class)
public class PgpMessageBuilderTest {
private static final long TEST_SIGN_KEY_ID = 123L;
private static final long TEST_SELF_ENCRYPT_KEY_ID = 234L;
private static final String TEST_MESSAGE_TEXT = "message text with a ☭ CCCP symbol";
private ComposeCryptoStatusBuilder cryptoStatusBuilder = createDefaultComposeCryptoStatusBuilder();
private OpenPgpApi openPgpApi = mock(OpenPgpApi.class);
private PgpMessageBuilder pgpMessageBuilder = createDefaultPgpMessageBuilder(openPgpApi);
@Test(expected = AssertionError.class)
public void build__withDisabledCrypto__shouldError() throws MessagingException {
pgpMessageBuilder.setCryptoStatus(cryptoStatusBuilder.setCryptoMode(CryptoMode.DISABLE).build());
pgpMessageBuilder.buildAsync(mock(Callback.class));
}
@Test
public void build__withCryptoProviderNotOk__shouldThrow() throws MessagingException {
cryptoStatusBuilder.setCryptoMode(CryptoMode.SIGN_ONLY);
CryptoProviderState[] cryptoProviderStates = {
CryptoProviderState.LOST_CONNECTION, CryptoProviderState.UNCONFIGURED,
CryptoProviderState.UNINITIALIZED, CryptoProviderState.ERROR
};
for (CryptoProviderState state : cryptoProviderStates) {
cryptoStatusBuilder.setCryptoProviderState(state);
pgpMessageBuilder.setCryptoStatus(cryptoStatusBuilder.build());
Callback mockCallback = mock(Callback.class);
pgpMessageBuilder.buildAsync(mockCallback);
verify(mockCallback).onMessageBuildException(any(MessagingException.class));
verifyNoMoreInteractions(mockCallback);
}
}
@Test
public void buildSign__withNoDetachedSignatureInResult__shouldThrow() throws MessagingException {
cryptoStatusBuilder.setCryptoMode(CryptoMode.SIGN_ONLY);
pgpMessageBuilder.setCryptoStatus(cryptoStatusBuilder.build());
Intent returnIntent = new Intent();
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
when(openPgpApi.executeApi(any(Intent.class), any(OpenPgpDataSource.class), any(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() throws MessagingException {
cryptoStatusBuilder.setCryptoMode(CryptoMode.SIGN_ONLY);
pgpMessageBuilder.setCryptoStatus(cryptoStatusBuilder.build());
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), any(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_SIGN_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));
Assert.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 });
}
@Test
public void buildSign__withUserInteractionResult__shouldReturnUserInteraction() throws MessagingException {
cryptoStatusBuilder.setCryptoMode(CryptoMode.SIGN_ONLY);
pgpMessageBuilder.setCryptoStatus(cryptoStatusBuilder.build());
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), any(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() throws MessagingException {
cryptoStatusBuilder.setCryptoMode(CryptoMode.SIGN_ONLY);
pgpMessageBuilder.setCryptoStatus(cryptoStatusBuilder.build());
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), any(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), any(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), any(OutputStream.class));
verify(returnIntent).getIntExtra(eq(OpenPgpApi.RESULT_CODE), anyInt());
}
}
@Test
public void buildEncrypt__withoutRecipients__shouldThrow() throws MessagingException {
cryptoStatusBuilder
.setCryptoMode(CryptoMode.OPPORTUNISTIC)
.setRecipients(new ArrayList<Recipient>());
pgpMessageBuilder.setCryptoStatus(cryptoStatusBuilder.build());
Intent returnIntent = spy(new Intent());
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
when(openPgpApi.executeApi(any(Intent.class), any(OpenPgpDataSource.class), any(OutputStream.class)))
.thenReturn(returnIntent);
Callback mockCallback = mock(Callback.class);
pgpMessageBuilder.buildAsync(mockCallback);
verify(mockCallback).onMessageBuildException(any(MessagingException.class));
verifyNoMoreInteractions(mockCallback);
}
@Test
public void buildEncrypt__shouldSucceed() throws MessagingException {
ComposeCryptoStatus cryptoStatus = cryptoStatusBuilder
.setCryptoMode(CryptoMode.PRIVATE)
.setRecipients(Collections.singletonList(new Recipient("test", "test@example.org", "labru", -1, "key")))
.build();
pgpMessageBuilder.setCryptoStatus(cryptoStatus);
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), any(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_SIGN_KEY_ID);
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, new long[] { TEST_SELF_ENCRYPT_KEY_ID });
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_ENCRYPT_OPPORTUNISTIC, false);
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());
Assert.assertTrue("message body must be BinaryTempFileBody",
encryptedBodyPart.getBody() instanceof BinaryTempFileBody);
Assert.assertEquals(MimeUtil.ENC_7BIT, ((BinaryTempFileBody) encryptedBodyPart.getBody()).getEncoding());
}
@Test
public void buildEncrypt__withInlineEnabled__shouldSucceed() throws MessagingException {
ComposeCryptoStatus cryptoStatus = cryptoStatusBuilder
.setCryptoMode(CryptoMode.PRIVATE)
.setRecipients(Collections.singletonList(new Recipient("test", "test@example.org", "labru", -1, "key")))
.setEnablePgpInline(true)
.build();
pgpMessageBuilder.setCryptoStatus(cryptoStatus);
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), any(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_SIGN_KEY_ID);
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, new long[] { TEST_SELF_ENCRYPT_KEY_ID });
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_ENCRYPT_OPPORTUNISTIC, false);
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());
Assert.assertTrue("message body must be BinaryTempFileBody", message.getBody() instanceof BinaryTempFileBody);
Assert.assertEquals(MimeUtil.ENC_7BIT, ((BinaryTempFileBody) message.getBody()).getEncoding());
}
@Test
public void buildSign__withInlineEnabled__shouldSucceed() throws MessagingException {
ComposeCryptoStatus cryptoStatus = cryptoStatusBuilder
.setCryptoMode(CryptoMode.SIGN_ONLY)
.setRecipients(Collections.singletonList(new Recipient("test", "test@example.org", "labru", -1, "key")))
.setEnablePgpInline(true)
.build();
pgpMessageBuilder.setCryptoStatus(cryptoStatus);
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), any(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_SIGN_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());
}
@Test
public void buildSignWithAttach__withInlineEnabled__shouldThrow() throws MessagingException {
ComposeCryptoStatus cryptoStatus = cryptoStatusBuilder
.setCryptoMode(CryptoMode.SIGN_ONLY)
.setEnablePgpInline(true)
.build();
pgpMessageBuilder.setCryptoStatus(cryptoStatus);
pgpMessageBuilder.setAttachments(Collections.singletonList(Attachment.createAttachment(null, 0, null)));
Callback mockCallback = mock(Callback.class);
pgpMessageBuilder.buildAsync(mockCallback);
verify(mockCallback).onMessageBuildException(any(MessagingException.class));
verifyNoMoreInteractions(mockCallback);
verifyNoMoreInteractions(openPgpApi);
}
@Test
public void buildEncryptWithAttach__withInlineEnabled__shouldThrow() throws MessagingException {
ComposeCryptoStatus cryptoStatus = cryptoStatusBuilder
.setCryptoMode(CryptoMode.OPPORTUNISTIC)
.setEnablePgpInline(true)
.build();
pgpMessageBuilder.setCryptoStatus(cryptoStatus);
pgpMessageBuilder.setAttachments(Collections.singletonList(Attachment.createAttachment(null, 0, null)));
Callback mockCallback = mock(Callback.class);
pgpMessageBuilder.buildAsync(mockCallback);
verify(mockCallback).onMessageBuildException(any(MessagingException.class));
verifyNoMoreInteractions(mockCallback);
verifyNoMoreInteractions(openPgpApi);
}
@Test
public void buildOpportunisticEncrypt__withNoKeysAndNoSignOnly__shouldNotBeSigned() throws MessagingException {
ComposeCryptoStatus cryptoStatus = cryptoStatusBuilder
.setRecipients(Collections.singletonList(new Recipient("test", "test@example.org", "labru", -1, "key")))
.setCryptoMode(CryptoMode.OPPORTUNISTIC)
.build();
pgpMessageBuilder.setCryptoStatus(cryptoStatus);
Intent returnIntent = new Intent();
returnIntent.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
returnIntent.putExtra(OpenPgpApi.RESULT_ERROR,
new OpenPgpError(OpenPgpError.OPPORTUNISTIC_MISSING_KEYS, "Missing keys"));
when(openPgpApi.executeApi(any(Intent.class), any(OpenPgpDataSource.class), any(OutputStream.class)))
.thenReturn(returnIntent);
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();
Assert.assertEquals("text/plain", message.getMimeType());
}
@Test
public void buildSign__withNoDetachedSignatureExtra__shouldFail() throws MessagingException {
ComposeCryptoStatus cryptoStatus = cryptoStatusBuilder
.setCryptoMode(CryptoMode.SIGN_ONLY)
.build();
pgpMessageBuilder.setCryptoStatus(cryptoStatus);
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), any(OutputStream.class)))
.thenReturn(returnIntentSigned);
Callback mockCallback = mock(Callback.class);
pgpMessageBuilder.buildAsync(mockCallback);
verify(mockCallback).onMessageBuildException(any(MessagingException.class));
verifyNoMoreInteractions(mockCallback);
}
private ComposeCryptoStatusBuilder createDefaultComposeCryptoStatusBuilder() {
return new ComposeCryptoStatusBuilder()
.setEnablePgpInline(false)
.setSigningKeyId(TEST_SIGN_KEY_ID)
.setSelfEncryptId(TEST_SELF_ENCRYPT_KEY_ID)
.setRecipients(new ArrayList<Recipient>())
.setCryptoProviderState(CryptoProviderState.OK);
}
private static PgpMessageBuilder createDefaultPgpMessageBuilder(OpenPgpApi openPgpApi) {
PgpMessageBuilder builder = new PgpMessageBuilder(
RuntimeEnvironment.application, MessageIdGenerator.getInstance(), BoundaryGenerator.getInstance());
builder.setOpenPgpApi(openPgpApi);
Identity identity = new Identity();
identity.setName("tester");
identity.setEmail("test@example.org");
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 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((long[]) expectedExtra, (long[]) intentExtra);
}
} else {
if (!intentExtra.equals(expectedExtra)) {
Assert.assertEquals(expectedExtra, intentExtra);
}
}
}
}
}