Add new AutoDiscovery API

This commit is contained in:
cketti 2023-05-22 14:27:32 +02:00
parent 3e5b7b1772
commit f948fad797
14 changed files with 198 additions and 1 deletions

View file

@ -0,0 +1,13 @@
package app.k9mail.core.common.net
/**
* Represents a hostname, IPv4, or IPv6 address.
*/
@JvmInline
value class Hostname(val value: String) {
init {
requireNotNull(HostNameUtils.isLegalHostNameOrIP(value)) { "Not a valid domain or IP: '$value'" }
}
}
fun String.toHostname() = Hostname(this)

View file

@ -0,0 +1,11 @@
package app.k9mail.core.common.net
@Suppress("MagicNumber")
@JvmInline
value class Port(val value: Int) {
init {
require(value in 0..65535) { "Not a valid port number: $value" }
}
}
fun Int.toPort() = Port(this)

View file

@ -0,0 +1,47 @@
package app.k9mail.core.common.net
import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.hasMessage
import assertk.assertions.isEqualTo
import assertk.assertions.isInstanceOf
import kotlin.test.Test
class HostnameTest {
@Test
fun `valid domain`() {
val hostname = Hostname("domain.example")
assertThat(hostname.value).isEqualTo("domain.example")
}
@Test
fun `valid IPv4`() {
val hostname = Hostname("127.0.0.1")
assertThat(hostname.value).isEqualTo("127.0.0.1")
}
@Test
fun `valid IPv6`() {
val hostname = Hostname("fc00::1")
assertThat(hostname.value).isEqualTo("fc00::1")
}
@Test
fun `invalid domain should throw`() {
assertFailure {
Hostname("invalid domain")
}.isInstanceOf<IllegalArgumentException>()
.hasMessage("Not a valid domain or IP: 'invalid domain'")
}
@Test
fun `invalid IPv6 should throw`() {
assertFailure {
Hostname("fc00:1")
}.isInstanceOf<IllegalArgumentException>()
.hasMessage("Not a valid domain or IP: 'fc00:1'")
}
}

View file

@ -0,0 +1,33 @@
package app.k9mail.core.common.net
import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.hasMessage
import assertk.assertions.isEqualTo
import assertk.assertions.isInstanceOf
import kotlin.test.Test
class PortTest {
@Test
fun `valid port number`() {
val port = Port(993)
assertThat(port.value).isEqualTo(993)
}
@Test
fun `negative port number should throw`() {
assertFailure {
Port(-1)
}.isInstanceOf<IllegalArgumentException>()
.hasMessage("Not a valid port number: -1")
}
@Test
fun `port number exceeding valid range should throw`() {
assertFailure {
Port(65536)
}.isInstanceOf<IllegalArgumentException>()
.hasMessage("Not a valid port number: 65536")
}
}

View file

@ -5,4 +5,5 @@ plugins {
dependencies {
api(projects.mail.common)
api(projects.core.common)
}

View file

@ -0,0 +1,13 @@
package app.k9mail.autodiscovery.api
/**
* The authentication types supported when using the [AutoDiscovery] mechanism.
*
* Note: Currently we support the same set of values in [ImapServerSettings] and [SmtpServerSettings]. As soon as this
* changes, this type should be replaced with `ImapAuthenticationType` and `SmtpAuthenticationType`.
*/
enum class AuthenticationType {
PasswordCleartext,
PasswordEncrypted,
OAuth2,
}

View file

@ -0,0 +1,13 @@
package app.k9mail.autodiscovery.api
import app.k9mail.core.common.mail.EmailAddress
/**
* Provides a mechanism to find mail server settings for a given email address.
*/
interface AutoDiscovery {
/**
* Returns a list of [AutoDiscoveryRunnable]s that perform the actual mail server settings discovery.
*/
fun initDiscovery(email: EmailAddress): List<AutoDiscoveryRunnable>
}

View file

@ -0,0 +1,23 @@
package app.k9mail.autodiscovery.api
/**
* Results of a mail server settings lookup.
*/
data class AutoDiscoveryResult(
val incomingServerSettings: IncomingServerSettings,
val outgoingServerSettings: OutgoingServerSettings,
)
/**
* Incoming mail server settings.
*
* Implementations contain protocol-specific properties.
*/
interface IncomingServerSettings
/**
* Outgoing mail server settings.
*
* Implementations contain protocol-specific properties.
*/
interface OutgoingServerSettings

View file

@ -0,0 +1,10 @@
package app.k9mail.autodiscovery.api
/**
* Performs a mail server settings lookup.
*
* This is an abstraction that allows us to run multiple lookups in parallel.
*/
fun interface AutoDiscoveryRunnable {
suspend fun run(): AutoDiscoveryResult?
}

View file

@ -0,0 +1,9 @@
package app.k9mail.autodiscovery.api
/**
* The connection security methods supported when using the [AutoDiscovery] mechanism.
*/
enum class ConnectionSecurity {
StartTLS,
TLS,
}

View file

@ -3,6 +3,7 @@ package app.k9mail.autodiscovery.api
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ConnectionSecurity
@Deprecated("New code should use app.k9mail.autodiscovery.api.AutoDiscovery")
interface ConnectionSettingsDiscovery {
fun discover(email: String): DiscoveryResults?
}

View file

@ -0,0 +1,12 @@
package app.k9mail.autodiscovery.api
import app.k9mail.core.common.net.Hostname
import app.k9mail.core.common.net.Port
data class ImapServerSettings(
val hostname: Hostname,
val port: Port,
val connectionSecurity: ConnectionSecurity,
val authenticationType: AuthenticationType,
val username: String,
) : IncomingServerSettings

View file

@ -0,0 +1,12 @@
package app.k9mail.autodiscovery.api
import app.k9mail.core.common.net.Hostname
import app.k9mail.core.common.net.Port
data class SmtpServerSettings(
val hostname: Hostname,
val port: Port,
val connectionSecurity: ConnectionSecurity,
val authenticationType: AuthenticationType,
val username: String,
) : OutgoingServerSettings

View file

@ -7,7 +7,6 @@ dependencies {
api(projects.feature.autodiscovery.api)
compileOnly(libs.xmlpull)
implementation(projects.core.common)
implementation(libs.okhttp)
implementation(libs.minidns.hla)