Loading app/core/src/main/java/com/fsck/k9/Account.java +11 −0 Original line number Diff line number Diff line Loading @@ -216,6 +216,7 @@ public class Account implements BaseAccount, StoreConfig { private boolean autocryptPreferEncryptMutual; private boolean openPgpHideSignOnly; private boolean openPgpEncryptSubject; private boolean openPgpEncryptAllDrafts; private boolean markMessageAsReadOnView; private boolean alwaysShowCcBcc; private boolean allowRemoteSearch; Loading Loading @@ -461,6 +462,7 @@ public class Account implements BaseAccount, StoreConfig { openPgpKey = storage.getLong(accountUuid + ".cryptoKey", NO_OPENPGP_KEY); openPgpHideSignOnly = storage.getBoolean(accountUuid + ".openPgpHideSignOnly", true); openPgpEncryptSubject = storage.getBoolean(accountUuid + ".openPgpEncryptSubject", true); openPgpEncryptAllDrafts = storage.getBoolean(accountUuid + ".openPgpEncryptAllDrafts", true); autocryptPreferEncryptMutual = storage.getBoolean(accountUuid + ".autocryptMutualMode", false); allowRemoteSearch = storage.getBoolean(accountUuid + ".allowRemoteSearch", false); remoteSearchFullText = storage.getBoolean(accountUuid + ".remoteSearchFullText", false); Loading Loading @@ -740,6 +742,7 @@ public class Account implements BaseAccount, StoreConfig { editor.putLong(accountUuid + ".cryptoKey", openPgpKey); editor.putBoolean(accountUuid + ".openPgpHideSignOnly", openPgpHideSignOnly); editor.putBoolean(accountUuid + ".openPgpEncryptSubject", openPgpEncryptSubject); editor.putBoolean(accountUuid + ".openPgpEncryptAllDrafts", openPgpEncryptAllDrafts); editor.putString(accountUuid + ".openPgpProvider", openPgpProvider); editor.putBoolean(accountUuid + ".autocryptMutualMode", autocryptPreferEncryptMutual); editor.putBoolean(accountUuid + ".allowRemoteSearch", allowRemoteSearch); Loading Loading @@ -1591,6 +1594,14 @@ public class Account implements BaseAccount, StoreConfig { this.openPgpEncryptSubject = openPgpEncryptSubject; } public boolean getOpenPgpEncryptAllDrafts() { return openPgpEncryptAllDrafts; } public void setOpenPgpEncryptAllDrafts(boolean openPgpEncryptAllDrafts) { this.openPgpEncryptAllDrafts = openPgpEncryptAllDrafts; } public boolean allowRemoteSearch() { return allowRemoteSearch; } Loading app/core/src/main/java/com/fsck/k9/autocrypt/AutocryptDraftStateHeader.kt 0 → 100644 +57 −0 Original line number Diff line number Diff line package com.fsck.k9.autocrypt import com.fsck.k9.message.CryptoStatus data class AutocryptDraftStateHeader(val isEncrypt: Boolean, val isSignOnly: Boolean, val isReply: Boolean, val isByChoice: Boolean, val isPgpInline: Boolean, val parameters: Map<String, String> = mapOf()) { fun toHeaderValue(): String { val builder = StringBuilder() builder.append(AutocryptDraftStateHeader.PARAM_ENCRYPT) builder.append(if (isEncrypt) "=yes; " else "=no; ") if (isReply) { builder.append(AutocryptDraftStateHeader.PARAM_IS_REPLY).append("=yes; ") } if (isSignOnly) { builder.append(AutocryptDraftStateHeader.PARAM_SIGN_ONLY).append("=yes; ") } if (isByChoice) { builder.append(AutocryptDraftStateHeader.PARAM_BY_CHOICE).append("=yes; ") } if (isPgpInline) { builder.append(AutocryptDraftStateHeader.PARAM_PGP_INLINE).append("=yes; ") } return builder.toString() } companion object { const val AUTOCRYPT_DRAFT_STATE_HEADER = "Autocrypt-Draft-State" const val PARAM_ENCRYPT = "encrypt" const val PARAM_IS_REPLY = "_is-reply-to-encrypted" const val PARAM_BY_CHOICE = "_by-choice" const val PARAM_PGP_INLINE = "_pgp-inline" const val PARAM_SIGN_ONLY = "_sign-only" const val VALUE_YES = "yes"; @JvmStatic fun fromCryptoStatus(cryptoStatus: CryptoStatus): AutocryptDraftStateHeader { if (cryptoStatus.isSignOnly) { return AutocryptDraftStateHeader(false, true, cryptoStatus.isReplyToEncrypted, cryptoStatus.isUserChoice(), cryptoStatus.isPgpInlineModeEnabled, mapOf()) } return AutocryptDraftStateHeader(cryptoStatus.isEncryptionEnabled, false, cryptoStatus.isReplyToEncrypted, cryptoStatus.isUserChoice(), cryptoStatus.isPgpInlineModeEnabled, mapOf()) } } } app/core/src/main/java/com/fsck/k9/autocrypt/AutocryptDraftStateHeaderParser.kt 0 → 100644 +42 −0 Original line number Diff line number Diff line package com.fsck.k9.autocrypt import com.fsck.k9.mail.internet.MimeUtility class AutocryptDraftStateHeaderParser internal constructor() { fun parseAutocryptDraftStateHeader(headerValue: String): AutocryptDraftStateHeader? { val parameters = MimeUtility.getAllHeaderParameters(headerValue) val isEncryptStr = parameters.remove(AutocryptDraftStateHeader.PARAM_ENCRYPT) ?: return null val isEncrypt = isEncryptStr == AutocryptDraftStateHeader.VALUE_YES val isSignOnlyStr = parameters.remove(AutocryptDraftStateHeader.PARAM_SIGN_ONLY) val isSignOnly = isSignOnlyStr == AutocryptDraftStateHeader.VALUE_YES val isReplyStr = parameters.remove(AutocryptDraftStateHeader.PARAM_IS_REPLY) val isReply = isReplyStr == AutocryptDraftStateHeader.VALUE_YES val isByChoiceStr = parameters.remove(AutocryptDraftStateHeader.PARAM_BY_CHOICE) val isByChoice = isByChoiceStr == AutocryptDraftStateHeader.VALUE_YES val isPgpInlineStr = parameters.remove(AutocryptDraftStateHeader.PARAM_PGP_INLINE) val isPgpInline = isPgpInlineStr == AutocryptDraftStateHeader.VALUE_YES if (hasCriticalParameters(parameters)) { return null } return AutocryptDraftStateHeader(isEncrypt, isSignOnly, isReply, isByChoice, isPgpInline, parameters) } private fun hasCriticalParameters(parameters: Map<String, String>): Boolean { for (parameterName in parameters.keys) { if (!parameterName.startsWith("_")) { return true } } return false } } app/core/src/main/java/com/fsck/k9/autocrypt/KoinModule.kt +1 −0 Original line number Diff line number Diff line Loading @@ -4,4 +4,5 @@ import org.koin.dsl.module.applicationContext val autocryptModule = applicationContext { bean { AutocryptTransferMessageCreator(get()) } bean { AutocryptDraftStateHeaderParser() } } No newline at end of file app/core/src/main/java/com/fsck/k9/controller/MessagingController.java +9 −4 Original line number Diff line number Diff line Loading @@ -1443,14 +1443,16 @@ public class MessagingController { */ public void sendMessage(final Account account, final Message message, String plaintextSubject, MessagingListener listener) { try { LocalStore localStore = account.getLocalStore(); LocalFolder localFolder = localStore.getFolder(account.getOutboxFolder()); localFolder.open(Folder.OPEN_MODE_RW); localFolder.appendMessages(Collections.singletonList(message)); Message localMessage = localFolder.getMessage(message.getUid()); LocalMessage localMessage = localFolder.getMessage(message.getUid()); localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true); localMessage.setCachedDecryptedSubject(plaintextSubject); localFolder.close(); sendPendingMessages(account, listener); } catch (Exception e) { Loading Loading @@ -1601,7 +1603,7 @@ public class MessagingController { localFolder.fetch(Collections.singletonList(message), fp, null); try { if (message.getHeader(K9.IDENTITY_HEADER).length > 0) { if (message.getHeader(K9.IDENTITY_HEADER).length > 0 || message.isSet(Flag.DRAFT)) { Timber.v("The user has set the Outbox and Drafts folder to the same thing. " + "This message appears to be a draft, so K-9 will not send it"); continue; Loading Loading @@ -2760,8 +2762,8 @@ public class MessagingController { * * @return Message representing the entry in the local store. */ public Message saveDraft(final Account account, final Message message, long existingDraftId, boolean saveRemotely) { Message localMessage = null; public Message saveDraft(final Account account, final Message message, long existingDraftId, String plaintextSubject, boolean saveRemotely) { LocalMessage localMessage = null; try { LocalStore localStore = account.getLocalStore(); LocalFolder localFolder = localStore.getFolder(account.getDraftsFolder()); Loading @@ -2777,6 +2779,9 @@ public class MessagingController { // Fetch the message back from the store. This is the Message that's returned to the caller. localMessage = localFolder.getMessage(message.getUid()); localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true); if (plaintextSubject != null) { localMessage.setCachedDecryptedSubject(plaintextSubject); } if (saveRemotely) { PendingCommand command = PendingAppend.create(localFolder.getServerId(), localMessage.getUid()); Loading Loading
app/core/src/main/java/com/fsck/k9/Account.java +11 −0 Original line number Diff line number Diff line Loading @@ -216,6 +216,7 @@ public class Account implements BaseAccount, StoreConfig { private boolean autocryptPreferEncryptMutual; private boolean openPgpHideSignOnly; private boolean openPgpEncryptSubject; private boolean openPgpEncryptAllDrafts; private boolean markMessageAsReadOnView; private boolean alwaysShowCcBcc; private boolean allowRemoteSearch; Loading Loading @@ -461,6 +462,7 @@ public class Account implements BaseAccount, StoreConfig { openPgpKey = storage.getLong(accountUuid + ".cryptoKey", NO_OPENPGP_KEY); openPgpHideSignOnly = storage.getBoolean(accountUuid + ".openPgpHideSignOnly", true); openPgpEncryptSubject = storage.getBoolean(accountUuid + ".openPgpEncryptSubject", true); openPgpEncryptAllDrafts = storage.getBoolean(accountUuid + ".openPgpEncryptAllDrafts", true); autocryptPreferEncryptMutual = storage.getBoolean(accountUuid + ".autocryptMutualMode", false); allowRemoteSearch = storage.getBoolean(accountUuid + ".allowRemoteSearch", false); remoteSearchFullText = storage.getBoolean(accountUuid + ".remoteSearchFullText", false); Loading Loading @@ -740,6 +742,7 @@ public class Account implements BaseAccount, StoreConfig { editor.putLong(accountUuid + ".cryptoKey", openPgpKey); editor.putBoolean(accountUuid + ".openPgpHideSignOnly", openPgpHideSignOnly); editor.putBoolean(accountUuid + ".openPgpEncryptSubject", openPgpEncryptSubject); editor.putBoolean(accountUuid + ".openPgpEncryptAllDrafts", openPgpEncryptAllDrafts); editor.putString(accountUuid + ".openPgpProvider", openPgpProvider); editor.putBoolean(accountUuid + ".autocryptMutualMode", autocryptPreferEncryptMutual); editor.putBoolean(accountUuid + ".allowRemoteSearch", allowRemoteSearch); Loading Loading @@ -1591,6 +1594,14 @@ public class Account implements BaseAccount, StoreConfig { this.openPgpEncryptSubject = openPgpEncryptSubject; } public boolean getOpenPgpEncryptAllDrafts() { return openPgpEncryptAllDrafts; } public void setOpenPgpEncryptAllDrafts(boolean openPgpEncryptAllDrafts) { this.openPgpEncryptAllDrafts = openPgpEncryptAllDrafts; } public boolean allowRemoteSearch() { return allowRemoteSearch; } Loading
app/core/src/main/java/com/fsck/k9/autocrypt/AutocryptDraftStateHeader.kt 0 → 100644 +57 −0 Original line number Diff line number Diff line package com.fsck.k9.autocrypt import com.fsck.k9.message.CryptoStatus data class AutocryptDraftStateHeader(val isEncrypt: Boolean, val isSignOnly: Boolean, val isReply: Boolean, val isByChoice: Boolean, val isPgpInline: Boolean, val parameters: Map<String, String> = mapOf()) { fun toHeaderValue(): String { val builder = StringBuilder() builder.append(AutocryptDraftStateHeader.PARAM_ENCRYPT) builder.append(if (isEncrypt) "=yes; " else "=no; ") if (isReply) { builder.append(AutocryptDraftStateHeader.PARAM_IS_REPLY).append("=yes; ") } if (isSignOnly) { builder.append(AutocryptDraftStateHeader.PARAM_SIGN_ONLY).append("=yes; ") } if (isByChoice) { builder.append(AutocryptDraftStateHeader.PARAM_BY_CHOICE).append("=yes; ") } if (isPgpInline) { builder.append(AutocryptDraftStateHeader.PARAM_PGP_INLINE).append("=yes; ") } return builder.toString() } companion object { const val AUTOCRYPT_DRAFT_STATE_HEADER = "Autocrypt-Draft-State" const val PARAM_ENCRYPT = "encrypt" const val PARAM_IS_REPLY = "_is-reply-to-encrypted" const val PARAM_BY_CHOICE = "_by-choice" const val PARAM_PGP_INLINE = "_pgp-inline" const val PARAM_SIGN_ONLY = "_sign-only" const val VALUE_YES = "yes"; @JvmStatic fun fromCryptoStatus(cryptoStatus: CryptoStatus): AutocryptDraftStateHeader { if (cryptoStatus.isSignOnly) { return AutocryptDraftStateHeader(false, true, cryptoStatus.isReplyToEncrypted, cryptoStatus.isUserChoice(), cryptoStatus.isPgpInlineModeEnabled, mapOf()) } return AutocryptDraftStateHeader(cryptoStatus.isEncryptionEnabled, false, cryptoStatus.isReplyToEncrypted, cryptoStatus.isUserChoice(), cryptoStatus.isPgpInlineModeEnabled, mapOf()) } } }
app/core/src/main/java/com/fsck/k9/autocrypt/AutocryptDraftStateHeaderParser.kt 0 → 100644 +42 −0 Original line number Diff line number Diff line package com.fsck.k9.autocrypt import com.fsck.k9.mail.internet.MimeUtility class AutocryptDraftStateHeaderParser internal constructor() { fun parseAutocryptDraftStateHeader(headerValue: String): AutocryptDraftStateHeader? { val parameters = MimeUtility.getAllHeaderParameters(headerValue) val isEncryptStr = parameters.remove(AutocryptDraftStateHeader.PARAM_ENCRYPT) ?: return null val isEncrypt = isEncryptStr == AutocryptDraftStateHeader.VALUE_YES val isSignOnlyStr = parameters.remove(AutocryptDraftStateHeader.PARAM_SIGN_ONLY) val isSignOnly = isSignOnlyStr == AutocryptDraftStateHeader.VALUE_YES val isReplyStr = parameters.remove(AutocryptDraftStateHeader.PARAM_IS_REPLY) val isReply = isReplyStr == AutocryptDraftStateHeader.VALUE_YES val isByChoiceStr = parameters.remove(AutocryptDraftStateHeader.PARAM_BY_CHOICE) val isByChoice = isByChoiceStr == AutocryptDraftStateHeader.VALUE_YES val isPgpInlineStr = parameters.remove(AutocryptDraftStateHeader.PARAM_PGP_INLINE) val isPgpInline = isPgpInlineStr == AutocryptDraftStateHeader.VALUE_YES if (hasCriticalParameters(parameters)) { return null } return AutocryptDraftStateHeader(isEncrypt, isSignOnly, isReply, isByChoice, isPgpInline, parameters) } private fun hasCriticalParameters(parameters: Map<String, String>): Boolean { for (parameterName in parameters.keys) { if (!parameterName.startsWith("_")) { return true } } return false } }
app/core/src/main/java/com/fsck/k9/autocrypt/KoinModule.kt +1 −0 Original line number Diff line number Diff line Loading @@ -4,4 +4,5 @@ import org.koin.dsl.module.applicationContext val autocryptModule = applicationContext { bean { AutocryptTransferMessageCreator(get()) } bean { AutocryptDraftStateHeaderParser() } } No newline at end of file
app/core/src/main/java/com/fsck/k9/controller/MessagingController.java +9 −4 Original line number Diff line number Diff line Loading @@ -1443,14 +1443,16 @@ public class MessagingController { */ public void sendMessage(final Account account, final Message message, String plaintextSubject, MessagingListener listener) { try { LocalStore localStore = account.getLocalStore(); LocalFolder localFolder = localStore.getFolder(account.getOutboxFolder()); localFolder.open(Folder.OPEN_MODE_RW); localFolder.appendMessages(Collections.singletonList(message)); Message localMessage = localFolder.getMessage(message.getUid()); LocalMessage localMessage = localFolder.getMessage(message.getUid()); localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true); localMessage.setCachedDecryptedSubject(plaintextSubject); localFolder.close(); sendPendingMessages(account, listener); } catch (Exception e) { Loading Loading @@ -1601,7 +1603,7 @@ public class MessagingController { localFolder.fetch(Collections.singletonList(message), fp, null); try { if (message.getHeader(K9.IDENTITY_HEADER).length > 0) { if (message.getHeader(K9.IDENTITY_HEADER).length > 0 || message.isSet(Flag.DRAFT)) { Timber.v("The user has set the Outbox and Drafts folder to the same thing. " + "This message appears to be a draft, so K-9 will not send it"); continue; Loading Loading @@ -2760,8 +2762,8 @@ public class MessagingController { * * @return Message representing the entry in the local store. */ public Message saveDraft(final Account account, final Message message, long existingDraftId, boolean saveRemotely) { Message localMessage = null; public Message saveDraft(final Account account, final Message message, long existingDraftId, String plaintextSubject, boolean saveRemotely) { LocalMessage localMessage = null; try { LocalStore localStore = account.getLocalStore(); LocalFolder localFolder = localStore.getFolder(account.getDraftsFolder()); Loading @@ -2777,6 +2779,9 @@ public class MessagingController { // Fetch the message back from the store. This is the Message that's returned to the caller. localMessage = localFolder.getMessage(message.getUid()); localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true); if (plaintextSubject != null) { localMessage.setCachedDecryptedSubject(plaintextSubject); } if (saveRemotely) { PendingCommand command = PendingAppend.create(localFolder.getServerId(), localMessage.getUid()); Loading