package com.fsck.k9.activity.compose; import java.util.ArrayList; import java.util.List; 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.activity.compose.RecipientPresenter.CryptoProviderState; import com.fsck.k9.view.RecipientSelectView.Recipient; import com.fsck.k9.view.RecipientSelectView.RecipientCryptoStatus; /** This is an immutable object which contains all relevant metadata entered * during e-mail composition to apply cryptographic operations before sending * or saving as draft. */ public class ComposeCryptoStatus { private CryptoProviderState cryptoProviderState; private CryptoMode cryptoMode; private boolean allKeysAvailable; private boolean allKeysVerified; private boolean hasRecipients; private Long signingKeyId; private Long selfEncryptKeyId; private String[] recipientAddresses; private boolean enablePgpInline; public long[] getEncryptKeyIds() { if (selfEncryptKeyId == null) { return null; } return new long[] { selfEncryptKeyId }; } public String[] getRecipientAddresses() { return recipientAddresses; } public Long getSigningKeyId() { return signingKeyId; } CryptoStatusDisplayType getCryptoStatusDisplayType() { switch (cryptoProviderState) { case UNCONFIGURED: return CryptoStatusDisplayType.UNCONFIGURED; case UNINITIALIZED: return CryptoStatusDisplayType.UNINITIALIZED; case LOST_CONNECTION: case ERROR: 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!"); } switch (cryptoMode) { case PRIVATE: if (!hasRecipients) { return CryptoStatusDisplayType.PRIVATE_EMPTY; } else if (allKeysAvailable && allKeysVerified) { return CryptoStatusDisplayType.PRIVATE_TRUSTED; } else if (allKeysAvailable) { return CryptoStatusDisplayType.PRIVATE_UNTRUSTED; } return CryptoStatusDisplayType.PRIVATE_NOKEY; case OPPORTUNISTIC: if (!hasRecipients) { return CryptoStatusDisplayType.OPPORTUNISTIC_EMPTY; } else if (allKeysAvailable && allKeysVerified) { return CryptoStatusDisplayType.OPPORTUNISTIC_TRUSTED; } else if (allKeysAvailable) { return CryptoStatusDisplayType.OPPORTUNISTIC_UNTRUSTED; } return CryptoStatusDisplayType.OPPORTUNISTIC_NOKEY; case SIGN_ONLY: return CryptoStatusDisplayType.SIGN_ONLY; case DISABLE: return CryptoStatusDisplayType.DISABLED; default: throw new AssertionError("all CryptoModes must be handled!"); } } CryptoSpecialModeDisplayType getCryptoSpecialModeDisplayType() { if (cryptoProviderState != CryptoProviderState.OK) { return CryptoSpecialModeDisplayType.NONE; } if (isSignOnly() && isPgpInlineModeEnabled()) { return CryptoSpecialModeDisplayType.SIGN_ONLY_PGP_INLINE; } if (isSignOnly()) { return CryptoSpecialModeDisplayType.SIGN_ONLY; } if (isPgpInlineModeEnabled()) { return CryptoSpecialModeDisplayType.PGP_INLINE; } return CryptoSpecialModeDisplayType.NONE; } public boolean shouldUsePgpMessageBuilder() { return cryptoProviderState != CryptoProviderState.UNCONFIGURED && cryptoMode != CryptoMode.DISABLE; } public boolean isEncryptionEnabled() { return cryptoMode == CryptoMode.PRIVATE || cryptoMode == CryptoMode.OPPORTUNISTIC; } public boolean isEncryptionOpportunistic() { return cryptoMode == CryptoMode.OPPORTUNISTIC; } public boolean isSignOnly() { return cryptoMode == CryptoMode.SIGN_ONLY; } public boolean isSigningEnabled() { return cryptoMode != CryptoMode.DISABLE && signingKeyId != null; } public boolean isPgpInlineModeEnabled() { return enablePgpInline; } public boolean isCryptoDisabled() { return cryptoMode == CryptoMode.DISABLE; } public boolean isProviderStateOk() { return cryptoProviderState == CryptoProviderState.OK; } public static class ComposeCryptoStatusBuilder { private CryptoProviderState cryptoProviderState; private CryptoMode cryptoMode; private Long signingKeyId; private Long selfEncryptKeyId; private List<Recipient> recipients; private Boolean enablePgpInline; public ComposeCryptoStatusBuilder setCryptoProviderState(CryptoProviderState cryptoProviderState) { this.cryptoProviderState = cryptoProviderState; return this; } public ComposeCryptoStatusBuilder setCryptoMode(CryptoMode cryptoMode) { this.cryptoMode = cryptoMode; return this; } public ComposeCryptoStatusBuilder setSigningKeyId(long signingKeyId) { this.signingKeyId = signingKeyId; return this; } public ComposeCryptoStatusBuilder setSelfEncryptId(long selfEncryptKeyId) { this.selfEncryptKeyId = selfEncryptKeyId; return this; } public ComposeCryptoStatusBuilder setRecipients(List<Recipient> recipients) { this.recipients = recipients; return this; } public ComposeCryptoStatusBuilder setEnablePgpInline(boolean cryptoEnableCompat) { this.enablePgpInline = cryptoEnableCompat; return this; } public ComposeCryptoStatus build() { if (cryptoProviderState == 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!"); } ArrayList<String> recipientAddresses = new ArrayList<>(); boolean allKeysAvailable = true; boolean allKeysVerified = true; boolean hasRecipients = !recipients.isEmpty(); for (Recipient recipient : recipients) { RecipientCryptoStatus cryptoStatus = recipient.getCryptoStatus(); recipientAddresses.add(recipient.address.getAddress()); if (cryptoStatus.isAvailable()) { if (cryptoStatus == RecipientCryptoStatus.AVAILABLE_UNTRUSTED) { allKeysVerified = false; } } else { allKeysAvailable = false; } } ComposeCryptoStatus result = new ComposeCryptoStatus(); result.cryptoProviderState = cryptoProviderState; result.cryptoMode = cryptoMode; result.recipientAddresses = recipientAddresses.toArray(new String[0]); result.allKeysAvailable = allKeysAvailable; result.allKeysVerified = allKeysVerified; result.hasRecipients = hasRecipients; result.signingKeyId = signingKeyId; result.selfEncryptKeyId = selfEncryptKeyId; result.enablePgpInline = enablePgpInline; return result; } } public enum SendErrorState { PROVIDER_ERROR, SIGN_KEY_NOT_CONFIGURED, PRIVATE_BUT_MISSING_KEYS } public SendErrorState getSendErrorStateOrNull() { if (cryptoProviderState != CryptoProviderState.OK) { // TODO: be more specific about this error return SendErrorState.PROVIDER_ERROR; } boolean isSignKeyMissing = signingKeyId == null; if (isSignKeyMissing) { return SendErrorState.SIGN_KEY_NOT_CONFIGURED; } boolean isPrivateModeAndNotAllKeysAvailable = cryptoMode == CryptoMode.PRIVATE && !allKeysAvailable; if (isPrivateModeAndNotAllKeysAvailable) { return SendErrorState.PRIVATE_BUT_MISSING_KEYS; } return null; } enum AttachErrorState { IS_INLINE } AttachErrorState getAttachErrorStateOrNull() { if (cryptoProviderState == CryptoProviderState.UNCONFIGURED) { return null; } if (enablePgpInline) { return AttachErrorState.IS_INLINE; } return null; } }