package com.fsck.k9.view; import android.support.annotation.AttrRes; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; import android.support.annotation.StringRes; import com.fsck.k9.R; import com.fsck.k9.mailstore.CryptoResultAnnotation; import org.openintents.openpgp.OpenPgpDecryptionResult; import org.openintents.openpgp.OpenPgpSignatureResult; public enum MessageCryptoDisplayStatus { LOADING ( R.attr.openpgp_grey, R.drawable.status_lock ), CANCELLED ( R.attr.openpgp_black, R.drawable.status_lock, R.string.crypto_msg_cancelled ), DISABLED ( R.attr.openpgp_grey, R.drawable.status_lock_disabled, R.string.crypto_msg_disabled ), UNENCRYPTED_SIGN_UNKNOWN ( R.attr.openpgp_black, R.drawable.status_signature_unverified_cutout, R.drawable.status_dots, R.string.crypto_msg_signed_unencrypted, R.string.crypto_msg_sign_unknown ), UNENCRYPTED_SIGN_VERIFIED ( R.attr.openpgp_blue, R.drawable.status_signature_verified_cutout, R.drawable.status_none_dots_3, R.string.crypto_msg_signed_unencrypted, R.string.crypto_msg_sign_verified ), UNENCRYPTED_SIGN_UNVERIFIED ( R.attr.openpgp_orange, R.drawable.status_signature_verified_cutout, R.drawable.status_none_dots_2, R.string.crypto_msg_signed_unencrypted, R.string.crypto_msg_sign_unverified ), UNENCRYPTED_SIGN_MISMATCH ( R.attr.openpgp_red, R.drawable.status_signature_verified_cutout, R.drawable.status_none_dots_1, R.string.crypto_msg_signed_unencrypted, R.string.crypto_msg_sign_mismatch ), UNENCRYPTED_SIGN_EXPIRED ( R.attr.openpgp_red, R.drawable.status_signature_verified_cutout, R.drawable.status_none_dots_1, R.string.crypto_msg_signed_unencrypted, R.string.crypto_msg_sign_expired ), UNENCRYPTED_SIGN_REVOKED ( R.attr.openpgp_red, R.drawable.status_signature_verified_cutout, R.drawable.status_none_dots_1, R.string.crypto_msg_signed_unencrypted, R.string.crypto_msg_sign_revoked ), UNENCRYPTED_SIGN_INSECURE ( R.attr.openpgp_red, R.drawable.status_signature_verified_cutout, R.drawable.status_none_dots_1, R.string.crypto_msg_signed_unencrypted, R.string.crypto_msg_sign_insecure ), UNENCRYPTED_SIGN_ERROR ( R.attr.openpgp_red, R.drawable.status_signature_verified_cutout, R.drawable.status_dots, R.string.crypto_msg_signed_error, null ), ENCRYPTED_SIGN_UNKNOWN ( R.attr.openpgp_black, R.drawable.status_lock_opportunistic, R.drawable.status_dots, R.string.crypto_msg_signed_encrypted, R.string.crypto_msg_sign_unknown ), ENCRYPTED_SIGN_VERIFIED ( R.attr.openpgp_green, R.drawable.status_lock, R.drawable.status_none_dots_3, R.string.crypto_msg_signed_encrypted, R.string.crypto_msg_sign_verified ), ENCRYPTED_SIGN_UNVERIFIED ( R.attr.openpgp_orange, R.drawable.status_lock, R.drawable.status_none_dots_2, R.string.crypto_msg_signed_encrypted, R.string.crypto_msg_sign_unverified ), ENCRYPTED_SIGN_MISMATCH ( R.attr.openpgp_red, R.drawable.status_lock, R.drawable.status_none_dots_1, R.string.crypto_msg_signed_encrypted, R.string.crypto_msg_sign_mismatch ), ENCRYPTED_SIGN_EXPIRED ( R.attr.openpgp_red, R.drawable.status_lock, R.drawable.status_none_dots_1, R.string.crypto_msg_signed_encrypted, R.string.crypto_msg_sign_expired ), ENCRYPTED_SIGN_REVOKED ( R.attr.openpgp_red, R.drawable.status_lock, R.drawable.status_none_dots_1, R.string.crypto_msg_signed_encrypted, R.string.crypto_msg_sign_revoked ), ENCRYPTED_SIGN_INSECURE ( R.attr.openpgp_red, R.drawable.status_lock, R.drawable.status_none_dots_1, R.string.crypto_msg_signed_encrypted, R.string.crypto_msg_sign_insecure ), ENCRYPTED_UNSIGNED ( R.attr.openpgp_red, R.drawable.status_lock, R.drawable.status_dots, R.string.crypto_msg_encrypted_unsigned, R.string.crypto_msg_unsigned_encrypted ), ENCRYPTED_SIGN_ERROR ( R.attr.openpgp_red, R.drawable.status_lock, R.drawable.status_dots, R.string.crypto_msg_signed_encrypted, R.string.crypto_msg_sign_error ), ENCRYPTED_ERROR ( R.attr.openpgp_red, R.drawable.status_lock_error, R.string.crypto_msg_encrypted_error ), ENCRYPTED_INSECURE ( R.attr.openpgp_red, R.drawable.status_lock_error, R.string.crypto_msg_encrypted_insecure ), INCOMPLETE_ENCRYPTED ( R.attr.openpgp_black, R.drawable.status_lock_opportunistic, R.string.crypto_msg_incomplete_encrypted ), INCOMPLETE_SIGNED ( R.attr.openpgp_black, R.drawable.status_signature_unverified_cutout, R.drawable.status_dots, R.string.crypto_msg_signed_unencrypted, R.string.crypto_msg_sign_incomplete ), ENCRYPTED_NO_PROVIDER ( R.attr.openpgp_red, R.drawable.status_lock_error, R.string.crypto_msg_unsupported_encrypted ), UNSUPPORTED_ENCRYPTED ( R.attr.openpgp_red, R.drawable.status_lock_error, R.string.crypto_msg_unsupported_encrypted ), UNSUPPORTED_SIGNED ( R.attr.openpgp_grey, R.drawable.status_lock_disabled, R.string.crypto_msg_unsupported_signed ), ; @AttrRes public final int colorAttr; @DrawableRes public final int statusIconRes; @DrawableRes public final Integer statusDotsRes; @StringRes public final Integer textResTop; @StringRes public final Integer textResBottom; MessageCryptoDisplayStatus(@AttrRes int colorAttr, @DrawableRes int statusIconRes, @DrawableRes Integer statusDotsRes, @StringRes int textResTop, @StringRes Integer textResBottom) { this.colorAttr = colorAttr; this.statusIconRes = statusIconRes; this.statusDotsRes = statusDotsRes; this.textResTop = textResTop; this.textResBottom = textResBottom; } MessageCryptoDisplayStatus(@AttrRes int colorAttr, @DrawableRes int statusIconRes, @StringRes int textResTop) { this.colorAttr = colorAttr; this.statusIconRes = statusIconRes; this.statusDotsRes = null; this.textResTop = textResTop; this.textResBottom = null; } MessageCryptoDisplayStatus(@AttrRes int colorAttr, @DrawableRes int statusIconRes) { this.colorAttr = colorAttr; this.statusIconRes = statusIconRes; this.statusDotsRes = null; this.textResTop = null; this.textResBottom = null; } @NonNull public static MessageCryptoDisplayStatus fromResultAnnotation(CryptoResultAnnotation cryptoResult) { if (cryptoResult == null) { return DISABLED; } switch (cryptoResult.getErrorType()) { case OPENPGP_OK: return getDisplayStatusForPgpResult(cryptoResult); case OPENPGP_ENCRYPTED_BUT_INCOMPLETE: return INCOMPLETE_ENCRYPTED; case OPENPGP_SIGNED_BUT_INCOMPLETE: return INCOMPLETE_SIGNED; case ENCRYPTED_BUT_UNSUPPORTED: return UNSUPPORTED_ENCRYPTED; case SIGNED_BUT_UNSUPPORTED: return UNSUPPORTED_SIGNED; case OPENPGP_UI_CANCELED: return CANCELLED; case OPENPGP_SIGNED_API_ERROR: return UNENCRYPTED_SIGN_ERROR; case OPENPGP_ENCRYPTED_API_ERROR: return ENCRYPTED_ERROR; case OPENPGP_ENCRYPTED_NO_PROVIDER: return ENCRYPTED_NO_PROVIDER; } throw new IllegalStateException("Unhandled case!"); } @NonNull private static MessageCryptoDisplayStatus getDisplayStatusForPgpResult(CryptoResultAnnotation cryptoResult) { OpenPgpSignatureResult signatureResult = cryptoResult.getOpenPgpSignatureResult(); OpenPgpDecryptionResult decryptionResult = cryptoResult.getOpenPgpDecryptionResult(); if (decryptionResult == null || signatureResult == null) { throw new AssertionError("Both OpenPGP results must be non-null at this point!"); } if (signatureResult.getResult() == OpenPgpSignatureResult.RESULT_NO_SIGNATURE && cryptoResult.hasEncapsulatedResult()) { CryptoResultAnnotation encapsulatedResult = cryptoResult.getEncapsulatedResult(); if (encapsulatedResult.isOpenPgpResult()) { signatureResult = encapsulatedResult.getOpenPgpSignatureResult(); if (signatureResult == null) { throw new AssertionError("OpenPGP must contain signature result at this point!"); } } } switch (decryptionResult.getResult()) { case OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED: return getStatusForPgpUnencryptedResult(signatureResult); case OpenPgpDecryptionResult.RESULT_ENCRYPTED: return getStatusForPgpEncryptedResult(signatureResult); case OpenPgpDecryptionResult.RESULT_INSECURE: return ENCRYPTED_INSECURE; } throw new AssertionError("all cases must be handled, this is a bug!"); } @NonNull private static MessageCryptoDisplayStatus getStatusForPgpEncryptedResult(OpenPgpSignatureResult signatureResult) { switch (signatureResult.getResult()) { case OpenPgpSignatureResult.RESULT_NO_SIGNATURE: return ENCRYPTED_UNSIGNED; case OpenPgpSignatureResult.RESULT_VALID_KEY_CONFIRMED: case OpenPgpSignatureResult.RESULT_VALID_KEY_UNCONFIRMED: switch (signatureResult.getSenderStatusResult()) { case USER_ID_CONFIRMED: return ENCRYPTED_SIGN_VERIFIED; case USER_ID_UNCONFIRMED: return ENCRYPTED_SIGN_UNVERIFIED; case USER_ID_MISSING: return ENCRYPTED_SIGN_MISMATCH; case UNKNOWN: return ENCRYPTED_SIGN_UNVERIFIED; } throw new IllegalStateException("unhandled encrypted result case!"); case OpenPgpSignatureResult.RESULT_KEY_MISSING: return ENCRYPTED_SIGN_UNKNOWN; case OpenPgpSignatureResult.RESULT_INVALID_SIGNATURE: return ENCRYPTED_SIGN_ERROR; case OpenPgpSignatureResult.RESULT_INVALID_KEY_EXPIRED: return ENCRYPTED_SIGN_EXPIRED; case OpenPgpSignatureResult.RESULT_INVALID_KEY_REVOKED: return ENCRYPTED_SIGN_REVOKED; case OpenPgpSignatureResult.RESULT_INVALID_KEY_INSECURE: return ENCRYPTED_SIGN_INSECURE; default: throw new IllegalStateException("unhandled encrypted result case!"); } } @NonNull private static MessageCryptoDisplayStatus getStatusForPgpUnencryptedResult(OpenPgpSignatureResult signatureResult) { switch (signatureResult.getResult()) { case OpenPgpSignatureResult.RESULT_NO_SIGNATURE: return DISABLED; case OpenPgpSignatureResult.RESULT_VALID_KEY_CONFIRMED: case OpenPgpSignatureResult.RESULT_VALID_KEY_UNCONFIRMED: switch (signatureResult.getSenderStatusResult()) { case USER_ID_CONFIRMED: return UNENCRYPTED_SIGN_VERIFIED; case USER_ID_UNCONFIRMED: return UNENCRYPTED_SIGN_UNVERIFIED; case USER_ID_MISSING: return UNENCRYPTED_SIGN_MISMATCH; case UNKNOWN: return UNENCRYPTED_SIGN_UNVERIFIED; } throw new IllegalStateException("unhandled encrypted result case!"); case OpenPgpSignatureResult.RESULT_KEY_MISSING: return UNENCRYPTED_SIGN_UNKNOWN; case OpenPgpSignatureResult.RESULT_INVALID_SIGNATURE: return UNENCRYPTED_SIGN_ERROR; case OpenPgpSignatureResult.RESULT_INVALID_KEY_EXPIRED: return UNENCRYPTED_SIGN_EXPIRED; case OpenPgpSignatureResult.RESULT_INVALID_KEY_REVOKED: return UNENCRYPTED_SIGN_REVOKED; case OpenPgpSignatureResult.RESULT_INVALID_KEY_INSECURE: return UNENCRYPTED_SIGN_INSECURE; default: throw new IllegalStateException("unhandled encrypted result case!"); } } public boolean hasAssociatedKey() { switch (this) { case ENCRYPTED_SIGN_UNKNOWN: case ENCRYPTED_SIGN_VERIFIED: case ENCRYPTED_SIGN_UNVERIFIED: case ENCRYPTED_SIGN_MISMATCH: case ENCRYPTED_SIGN_EXPIRED: case ENCRYPTED_SIGN_REVOKED: case ENCRYPTED_SIGN_INSECURE: case UNENCRYPTED_SIGN_UNKNOWN: case UNENCRYPTED_SIGN_VERIFIED: case UNENCRYPTED_SIGN_UNVERIFIED: case UNENCRYPTED_SIGN_MISMATCH: case UNENCRYPTED_SIGN_EXPIRED: case UNENCRYPTED_SIGN_REVOKED: case UNENCRYPTED_SIGN_INSECURE: return true; } return false; } public boolean isUnencryptedSigned() { switch (this) { case UNENCRYPTED_SIGN_ERROR: case UNENCRYPTED_SIGN_UNKNOWN: case UNENCRYPTED_SIGN_VERIFIED: case UNENCRYPTED_SIGN_UNVERIFIED: case UNENCRYPTED_SIGN_MISMATCH: case UNENCRYPTED_SIGN_EXPIRED: case UNENCRYPTED_SIGN_REVOKED: case UNENCRYPTED_SIGN_INSECURE: return true; } return false; } }