Loading app/core/src/main/java/com/fsck/k9/Account.java +11 −10 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ import android.text.TextUtils; import com.fsck.k9.backend.api.SyncConfig.ExpungePolicy; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.NetworkType; import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mailstore.StorageManager; import com.fsck.k9.mailstore.StorageManager.StorageProvider; import org.jetbrains.annotations.NotNull; Loading Loading @@ -101,14 +102,14 @@ public class Account implements BaseAccount { private DeletePolicy deletePolicy = DeletePolicy.NEVER; private final String accountUuid; private String storeUri; private ServerSettings incomingServerSettings; private ServerSettings outgoingServerSettings; /** * Storage provider ID, used to locate and manage the underlying DB/file * storage */ private String localStorageProviderId; private String transportUri; private String description; private String alwaysBcc; private int automaticCheckIntervalMinutes; Loading Loading @@ -254,20 +255,20 @@ public class Account implements BaseAccount { return accountUuid; } public synchronized String getStoreUri() { return storeUri; public synchronized ServerSettings getIncomingServerSettings() { return incomingServerSettings; } public synchronized void setStoreUri(String storeUri) { this.storeUri = storeUri; public synchronized void setIncomingServerSettings(ServerSettings incomingServerSettings) { this.incomingServerSettings = incomingServerSettings; } public synchronized String getTransportUri() { return transportUri; public synchronized ServerSettings getOutgoingServerSettings() { return outgoingServerSettings; } public synchronized void setTransportUri(String transportUri) { this.transportUri = transportUri; public synchronized void setOutgoingServerSettings(ServerSettings outgoingServerSettings) { this.outgoingServerSettings = outgoingServerSettings; } @Override Loading app/core/src/main/java/com/fsck/k9/AccountPreferenceSerializer.kt +14 −10 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ import com.fsck.k9.Account.SpecialFolderSelection import com.fsck.k9.Account.UNASSIGNED_ACCOUNT_NUMBER import com.fsck.k9.helper.Utility import com.fsck.k9.mail.NetworkType import com.fsck.k9.mail.filter.Base64 import com.fsck.k9.mailstore.StorageManager import com.fsck.k9.preferences.Storage import com.fsck.k9.preferences.StorageEditor Loading @@ -24,16 +23,21 @@ import timber.log.Timber class AccountPreferenceSerializer( private val storageManager: StorageManager, private val resourceProvider: CoreResourceProvider private val resourceProvider: CoreResourceProvider, private val serverSettingsSerializer: ServerSettingsSerializer ) { @Synchronized fun loadAccount(account: Account, storage: Storage) { val accountUuid = account.uuid with(account) { storeUri = Base64.decode(storage.getString("$accountUuid.storeUri", null)) incomingServerSettings = serverSettingsSerializer.deserialize( storage.getString("$accountUuid.$INCOMING_SERVER_SETTINGS_KEY", "") ) outgoingServerSettings = serverSettingsSerializer.deserialize( storage.getString("$accountUuid.$OUTGOING_SERVER_SETTINGS_KEY", "") ) localStorageProviderId = storage.getString("$accountUuid.localStorageProvider", storageManager.defaultProviderId) transportUri = Base64.decode(storage.getString("$accountUuid.transportUri", null)) description = storage.getString("$accountUuid.description", null) alwaysBcc = storage.getString("$accountUuid.alwaysBcc", alwaysBcc) automaticCheckIntervalMinutes = storage.getInt("$accountUuid.automaticCheckIntervalMinutes", DEFAULT_SYNC_INTERVAL) Loading Loading @@ -244,9 +248,9 @@ class AccountPreferenceSerializer( } with(account) { editor.putString("$accountUuid.storeUri", Base64.encode(storeUri)) editor.putString("$accountUuid.$INCOMING_SERVER_SETTINGS_KEY", serverSettingsSerializer.serialize(incomingServerSettings)) editor.putString("$accountUuid.$OUTGOING_SERVER_SETTINGS_KEY", serverSettingsSerializer.serialize(outgoingServerSettings)) editor.putString("$accountUuid.localStorageProvider", localStorageProviderId) editor.putString("$accountUuid.transportUri", Base64.encode(transportUri)) editor.putString("$accountUuid.description", description) editor.putString("$accountUuid.alwaysBcc", alwaysBcc) editor.putInt("$accountUuid.automaticCheckIntervalMinutes", automaticCheckIntervalMinutes) Loading Loading @@ -372,8 +376,8 @@ class AccountPreferenceSerializer( editor.putString("accountUuids", accountUuids) } editor.remove("$accountUuid.storeUri") editor.remove("$accountUuid.transportUri") editor.remove("$accountUuid.$INCOMING_SERVER_SETTINGS_KEY") editor.remove("$accountUuid.$OUTGOING_SERVER_SETTINGS_KEY") editor.remove("$accountUuid.description") editor.remove("$accountUuid.name") editor.remove("$accountUuid.email") Loading Loading @@ -640,8 +644,8 @@ class AccountPreferenceSerializer( companion object { const val ACCOUNT_DESCRIPTION_KEY = "description" const val STORE_URI_KEY = "storeUri" const val TRANSPORT_URI_KEY = "transportUri" const val INCOMING_SERVER_SETTINGS_KEY = "incomingServerSettings" const val OUTGOING_SERVER_SETTINGS_KEY = "outgoingServerSettings" const val IDENTITY_NAME_KEY = "name" const val IDENTITY_EMAIL_KEY = "email" Loading app/core/src/main/java/com/fsck/k9/KoinModule.kt +1 −0 Original line number Diff line number Diff line Loading @@ -26,4 +26,5 @@ val mainModule = module { single { Clock.INSTANCE } factory { ServerNameSuggester() } factory { EmailAddressValidator() } factory { ServerSettingsSerializer() } } app/core/src/main/java/com/fsck/k9/LocalKeyStoreManager.kt +14 −18 Original line number Diff line number Diff line package com.fsck.k9 import android.net.Uri import com.fsck.k9.mail.MailServerDirection import com.fsck.k9.mail.ssl.LocalKeyStore import java.security.cert.CertificateException Loading @@ -14,12 +13,12 @@ class LocalKeyStoreManager( */ @Throws(CertificateException::class) fun addCertificate(account: Account, direction: MailServerDirection, certificate: X509Certificate) { val uri = if (direction === MailServerDirection.INCOMING) { Uri.parse(account.storeUri) val serverSettings = if (direction === MailServerDirection.INCOMING) { account.incomingServerSettings } else { Uri.parse(account.transportUri) account.outgoingServerSettings } localKeyStore.addCertificate(uri.host, uri.port, certificate) localKeyStore.addCertificate(serverSettings.host, serverSettings.port, certificate) } /** Loading @@ -28,13 +27,13 @@ class LocalKeyStoreManager( * old host/port. */ fun deleteCertificate(account: Account, newHost: String, newPort: Int, direction: MailServerDirection) { val uri = if (direction === MailServerDirection.INCOMING) { Uri.parse(account.storeUri) val serverSettings = if (direction === MailServerDirection.INCOMING) { account.incomingServerSettings } else { Uri.parse(account.transportUri) account.outgoingServerSettings } val oldHost = uri.host val oldPort = uri.port val oldHost = serverSettings.host val oldPort = serverSettings.port if (oldPort == -1) { // This occurs when a new account is created return Loading @@ -49,15 +48,12 @@ class LocalKeyStoreManager( * certificates for the incoming and outgoing servers. */ fun deleteCertificates(account: Account) { val storeUri = account.storeUri if (storeUri != null) { val uri = Uri.parse(storeUri) localKeyStore.deleteCertificate(uri.host, uri.port) } val transportUri = account.transportUri if (transportUri != null) { val uri = Uri.parse(transportUri) localKeyStore.deleteCertificate(uri.host, uri.port) account.incomingServerSettings?.let { serverSettings -> localKeyStore.deleteCertificate(serverSettings.host, serverSettings.port) } account.outgoingServerSettings?.let { serverSettings -> localKeyStore.deleteCertificate(serverSettings.host, serverSettings.port) } } } app/core/src/main/java/com/fsck/k9/ServerSettingsSerializer.kt 0 → 100644 +122 −0 Original line number Diff line number Diff line package com.fsck.k9 import com.fsck.k9.mail.AuthType import com.fsck.k9.mail.ConnectionSecurity import com.fsck.k9.mail.ServerSettings import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonReader.Token import com.squareup.moshi.JsonWriter class ServerSettingsSerializer { private val adapter = ServerSettingsAdapter() fun serialize(serverSettings: ServerSettings): String { return adapter.toJson(serverSettings) } fun deserialize(json: String): ServerSettings { return adapter.fromJson(json)!! } } private const val KEY_TYPE = "type" private const val KEY_HOST = "host" private const val KEY_PORT = "port" private const val KEY_CONNECTION_SECURITY = "connectionSecurity" private const val KEY_AUTHENTICATION_TYPE = "authenticationType" private const val KEY_USERNAME = "username" private const val KEY_PASSWORD = "password" private const val KEY_CLIENT_CERTIFICATE_ALIAS = "clientCertificateAlias" private val JSON_KEYS = JsonReader.Options.of( KEY_TYPE, KEY_HOST, KEY_PORT, KEY_CONNECTION_SECURITY, KEY_AUTHENTICATION_TYPE, KEY_USERNAME, KEY_PASSWORD, KEY_CLIENT_CERTIFICATE_ALIAS ) private class ServerSettingsAdapter : JsonAdapter<ServerSettings>() { override fun fromJson(reader: JsonReader): ServerSettings { reader.beginObject() var type: String? = null var host: String? = null var port: Int? = null var connectionSecurity: ConnectionSecurity? = null var authenticationType: AuthType? = null var username: String? = null var password: String? = null var clientCertificateAlias: String? = null val extra = mutableMapOf<String, String?>() while (reader.hasNext()) { when (reader.selectName(JSON_KEYS)) { 0 -> type = reader.nextString() 1 -> host = reader.nextString() 2 -> port = reader.nextInt() 3 -> connectionSecurity = ConnectionSecurity.valueOf(reader.nextString()) 4 -> authenticationType = AuthType.valueOf(reader.nextString()) 5 -> username = reader.nextString() 6 -> password = reader.nextStringOrNull() 7 -> clientCertificateAlias = reader.nextStringOrNull() else -> { val key = reader.nextName() val value = reader.nextStringOrNull() extra[key] = value } } } reader.endObject() requireNotNull(type) { "'type' must not be missing" } requireNotNull(host) { "'host' must not be missing" } requireNotNull(port) { "'port' must not be missing" } requireNotNull(connectionSecurity) { "'connectionSecurity' must not be missing" } requireNotNull(authenticationType) { "'authenticationType' must not be missing" } requireNotNull(username) { "'username' must not be missing" } return ServerSettings( type, host, port, connectionSecurity, authenticationType, username, password, clientCertificateAlias, extra ) } override fun toJson(writer: JsonWriter, serverSettings: ServerSettings?) { requireNotNull(serverSettings) writer.beginObject() writer.serializeNulls = true writer.name(KEY_TYPE).value(serverSettings.type) writer.name(KEY_HOST).value(serverSettings.host) writer.name(KEY_PORT).value(serverSettings.port) writer.name(KEY_CONNECTION_SECURITY).value(serverSettings.connectionSecurity.name) writer.name(KEY_AUTHENTICATION_TYPE).value(serverSettings.authenticationType.name) writer.name(KEY_USERNAME).value(serverSettings.username) writer.name(KEY_PASSWORD).value(serverSettings.password) writer.name(KEY_CLIENT_CERTIFICATE_ALIAS).value(serverSettings.clientCertificateAlias) for ((key, value) in serverSettings.extra) { writer.name(key).value(value) } writer.endObject() } private fun JsonReader.nextStringOrNull(): String? { return if (peek() == Token.NULL) nextNull() else nextString() } } Loading
app/core/src/main/java/com/fsck/k9/Account.java +11 −10 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ import android.text.TextUtils; import com.fsck.k9.backend.api.SyncConfig.ExpungePolicy; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.NetworkType; import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mailstore.StorageManager; import com.fsck.k9.mailstore.StorageManager.StorageProvider; import org.jetbrains.annotations.NotNull; Loading Loading @@ -101,14 +102,14 @@ public class Account implements BaseAccount { private DeletePolicy deletePolicy = DeletePolicy.NEVER; private final String accountUuid; private String storeUri; private ServerSettings incomingServerSettings; private ServerSettings outgoingServerSettings; /** * Storage provider ID, used to locate and manage the underlying DB/file * storage */ private String localStorageProviderId; private String transportUri; private String description; private String alwaysBcc; private int automaticCheckIntervalMinutes; Loading Loading @@ -254,20 +255,20 @@ public class Account implements BaseAccount { return accountUuid; } public synchronized String getStoreUri() { return storeUri; public synchronized ServerSettings getIncomingServerSettings() { return incomingServerSettings; } public synchronized void setStoreUri(String storeUri) { this.storeUri = storeUri; public synchronized void setIncomingServerSettings(ServerSettings incomingServerSettings) { this.incomingServerSettings = incomingServerSettings; } public synchronized String getTransportUri() { return transportUri; public synchronized ServerSettings getOutgoingServerSettings() { return outgoingServerSettings; } public synchronized void setTransportUri(String transportUri) { this.transportUri = transportUri; public synchronized void setOutgoingServerSettings(ServerSettings outgoingServerSettings) { this.outgoingServerSettings = outgoingServerSettings; } @Override Loading
app/core/src/main/java/com/fsck/k9/AccountPreferenceSerializer.kt +14 −10 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ import com.fsck.k9.Account.SpecialFolderSelection import com.fsck.k9.Account.UNASSIGNED_ACCOUNT_NUMBER import com.fsck.k9.helper.Utility import com.fsck.k9.mail.NetworkType import com.fsck.k9.mail.filter.Base64 import com.fsck.k9.mailstore.StorageManager import com.fsck.k9.preferences.Storage import com.fsck.k9.preferences.StorageEditor Loading @@ -24,16 +23,21 @@ import timber.log.Timber class AccountPreferenceSerializer( private val storageManager: StorageManager, private val resourceProvider: CoreResourceProvider private val resourceProvider: CoreResourceProvider, private val serverSettingsSerializer: ServerSettingsSerializer ) { @Synchronized fun loadAccount(account: Account, storage: Storage) { val accountUuid = account.uuid with(account) { storeUri = Base64.decode(storage.getString("$accountUuid.storeUri", null)) incomingServerSettings = serverSettingsSerializer.deserialize( storage.getString("$accountUuid.$INCOMING_SERVER_SETTINGS_KEY", "") ) outgoingServerSettings = serverSettingsSerializer.deserialize( storage.getString("$accountUuid.$OUTGOING_SERVER_SETTINGS_KEY", "") ) localStorageProviderId = storage.getString("$accountUuid.localStorageProvider", storageManager.defaultProviderId) transportUri = Base64.decode(storage.getString("$accountUuid.transportUri", null)) description = storage.getString("$accountUuid.description", null) alwaysBcc = storage.getString("$accountUuid.alwaysBcc", alwaysBcc) automaticCheckIntervalMinutes = storage.getInt("$accountUuid.automaticCheckIntervalMinutes", DEFAULT_SYNC_INTERVAL) Loading Loading @@ -244,9 +248,9 @@ class AccountPreferenceSerializer( } with(account) { editor.putString("$accountUuid.storeUri", Base64.encode(storeUri)) editor.putString("$accountUuid.$INCOMING_SERVER_SETTINGS_KEY", serverSettingsSerializer.serialize(incomingServerSettings)) editor.putString("$accountUuid.$OUTGOING_SERVER_SETTINGS_KEY", serverSettingsSerializer.serialize(outgoingServerSettings)) editor.putString("$accountUuid.localStorageProvider", localStorageProviderId) editor.putString("$accountUuid.transportUri", Base64.encode(transportUri)) editor.putString("$accountUuid.description", description) editor.putString("$accountUuid.alwaysBcc", alwaysBcc) editor.putInt("$accountUuid.automaticCheckIntervalMinutes", automaticCheckIntervalMinutes) Loading Loading @@ -372,8 +376,8 @@ class AccountPreferenceSerializer( editor.putString("accountUuids", accountUuids) } editor.remove("$accountUuid.storeUri") editor.remove("$accountUuid.transportUri") editor.remove("$accountUuid.$INCOMING_SERVER_SETTINGS_KEY") editor.remove("$accountUuid.$OUTGOING_SERVER_SETTINGS_KEY") editor.remove("$accountUuid.description") editor.remove("$accountUuid.name") editor.remove("$accountUuid.email") Loading Loading @@ -640,8 +644,8 @@ class AccountPreferenceSerializer( companion object { const val ACCOUNT_DESCRIPTION_KEY = "description" const val STORE_URI_KEY = "storeUri" const val TRANSPORT_URI_KEY = "transportUri" const val INCOMING_SERVER_SETTINGS_KEY = "incomingServerSettings" const val OUTGOING_SERVER_SETTINGS_KEY = "outgoingServerSettings" const val IDENTITY_NAME_KEY = "name" const val IDENTITY_EMAIL_KEY = "email" Loading
app/core/src/main/java/com/fsck/k9/KoinModule.kt +1 −0 Original line number Diff line number Diff line Loading @@ -26,4 +26,5 @@ val mainModule = module { single { Clock.INSTANCE } factory { ServerNameSuggester() } factory { EmailAddressValidator() } factory { ServerSettingsSerializer() } }
app/core/src/main/java/com/fsck/k9/LocalKeyStoreManager.kt +14 −18 Original line number Diff line number Diff line package com.fsck.k9 import android.net.Uri import com.fsck.k9.mail.MailServerDirection import com.fsck.k9.mail.ssl.LocalKeyStore import java.security.cert.CertificateException Loading @@ -14,12 +13,12 @@ class LocalKeyStoreManager( */ @Throws(CertificateException::class) fun addCertificate(account: Account, direction: MailServerDirection, certificate: X509Certificate) { val uri = if (direction === MailServerDirection.INCOMING) { Uri.parse(account.storeUri) val serverSettings = if (direction === MailServerDirection.INCOMING) { account.incomingServerSettings } else { Uri.parse(account.transportUri) account.outgoingServerSettings } localKeyStore.addCertificate(uri.host, uri.port, certificate) localKeyStore.addCertificate(serverSettings.host, serverSettings.port, certificate) } /** Loading @@ -28,13 +27,13 @@ class LocalKeyStoreManager( * old host/port. */ fun deleteCertificate(account: Account, newHost: String, newPort: Int, direction: MailServerDirection) { val uri = if (direction === MailServerDirection.INCOMING) { Uri.parse(account.storeUri) val serverSettings = if (direction === MailServerDirection.INCOMING) { account.incomingServerSettings } else { Uri.parse(account.transportUri) account.outgoingServerSettings } val oldHost = uri.host val oldPort = uri.port val oldHost = serverSettings.host val oldPort = serverSettings.port if (oldPort == -1) { // This occurs when a new account is created return Loading @@ -49,15 +48,12 @@ class LocalKeyStoreManager( * certificates for the incoming and outgoing servers. */ fun deleteCertificates(account: Account) { val storeUri = account.storeUri if (storeUri != null) { val uri = Uri.parse(storeUri) localKeyStore.deleteCertificate(uri.host, uri.port) } val transportUri = account.transportUri if (transportUri != null) { val uri = Uri.parse(transportUri) localKeyStore.deleteCertificate(uri.host, uri.port) account.incomingServerSettings?.let { serverSettings -> localKeyStore.deleteCertificate(serverSettings.host, serverSettings.port) } account.outgoingServerSettings?.let { serverSettings -> localKeyStore.deleteCertificate(serverSettings.host, serverSettings.port) } } }
app/core/src/main/java/com/fsck/k9/ServerSettingsSerializer.kt 0 → 100644 +122 −0 Original line number Diff line number Diff line package com.fsck.k9 import com.fsck.k9.mail.AuthType import com.fsck.k9.mail.ConnectionSecurity import com.fsck.k9.mail.ServerSettings import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonReader.Token import com.squareup.moshi.JsonWriter class ServerSettingsSerializer { private val adapter = ServerSettingsAdapter() fun serialize(serverSettings: ServerSettings): String { return adapter.toJson(serverSettings) } fun deserialize(json: String): ServerSettings { return adapter.fromJson(json)!! } } private const val KEY_TYPE = "type" private const val KEY_HOST = "host" private const val KEY_PORT = "port" private const val KEY_CONNECTION_SECURITY = "connectionSecurity" private const val KEY_AUTHENTICATION_TYPE = "authenticationType" private const val KEY_USERNAME = "username" private const val KEY_PASSWORD = "password" private const val KEY_CLIENT_CERTIFICATE_ALIAS = "clientCertificateAlias" private val JSON_KEYS = JsonReader.Options.of( KEY_TYPE, KEY_HOST, KEY_PORT, KEY_CONNECTION_SECURITY, KEY_AUTHENTICATION_TYPE, KEY_USERNAME, KEY_PASSWORD, KEY_CLIENT_CERTIFICATE_ALIAS ) private class ServerSettingsAdapter : JsonAdapter<ServerSettings>() { override fun fromJson(reader: JsonReader): ServerSettings { reader.beginObject() var type: String? = null var host: String? = null var port: Int? = null var connectionSecurity: ConnectionSecurity? = null var authenticationType: AuthType? = null var username: String? = null var password: String? = null var clientCertificateAlias: String? = null val extra = mutableMapOf<String, String?>() while (reader.hasNext()) { when (reader.selectName(JSON_KEYS)) { 0 -> type = reader.nextString() 1 -> host = reader.nextString() 2 -> port = reader.nextInt() 3 -> connectionSecurity = ConnectionSecurity.valueOf(reader.nextString()) 4 -> authenticationType = AuthType.valueOf(reader.nextString()) 5 -> username = reader.nextString() 6 -> password = reader.nextStringOrNull() 7 -> clientCertificateAlias = reader.nextStringOrNull() else -> { val key = reader.nextName() val value = reader.nextStringOrNull() extra[key] = value } } } reader.endObject() requireNotNull(type) { "'type' must not be missing" } requireNotNull(host) { "'host' must not be missing" } requireNotNull(port) { "'port' must not be missing" } requireNotNull(connectionSecurity) { "'connectionSecurity' must not be missing" } requireNotNull(authenticationType) { "'authenticationType' must not be missing" } requireNotNull(username) { "'username' must not be missing" } return ServerSettings( type, host, port, connectionSecurity, authenticationType, username, password, clientCertificateAlias, extra ) } override fun toJson(writer: JsonWriter, serverSettings: ServerSettings?) { requireNotNull(serverSettings) writer.beginObject() writer.serializeNulls = true writer.name(KEY_TYPE).value(serverSettings.type) writer.name(KEY_HOST).value(serverSettings.host) writer.name(KEY_PORT).value(serverSettings.port) writer.name(KEY_CONNECTION_SECURITY).value(serverSettings.connectionSecurity.name) writer.name(KEY_AUTHENTICATION_TYPE).value(serverSettings.authenticationType.name) writer.name(KEY_USERNAME).value(serverSettings.username) writer.name(KEY_PASSWORD).value(serverSettings.password) writer.name(KEY_CLIENT_CERTIFICATE_ALIAS).value(serverSettings.clientCertificateAlias) for ((key, value) in serverSettings.extra) { writer.name(key).value(value) } writer.endObject() } private fun JsonReader.nextStringOrNull(): String? { return if (peek() == Token.NULL) nextNull() else nextString() } }