From c585e05bc28edae2b73650505371bce6bbca9c51 Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 27 Apr 2023 16:51:59 +0200 Subject: [PATCH 1/3] Make `AutoconfigUrlProvider` an interface Have different implementations for an autoconfig server hosted by the email provider (`ProviderAutoconfigUrlProvider`) and the ISP DB (`IspDbAutoconfigUrlProvider`). --- .../autoconfig/AutoconfigUrlProvider.kt | 44 +------------------ .../autoconfig/IspDbAutoconfigUrlProvider.kt | 22 ++++++++++ .../ProviderAutoconfigUrlProvider.kt | 37 ++++++++++++++++ .../IspDbAutoconfigUrlProviderTest.kt | 18 ++++++++ ...t => ProviderAutoconfigUrlProviderTest.kt} | 5 +-- 5 files changed, 81 insertions(+), 45 deletions(-) create mode 100644 feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/IspDbAutoconfigUrlProvider.kt create mode 100644 feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProvider.kt create mode 100644 feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/IspDbAutoconfigUrlProviderTest.kt rename feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/{AutoconfigUrlProviderTest.kt => ProviderAutoconfigUrlProviderTest.kt} (80%) diff --git a/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/AutoconfigUrlProvider.kt b/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/AutoconfigUrlProvider.kt index c0f2b1460..94b3451ad 100644 --- a/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/AutoconfigUrlProvider.kt +++ b/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/AutoconfigUrlProvider.kt @@ -1,47 +1,7 @@ package app.k9mail.autodiscovery.autoconfig -import com.fsck.k9.helper.EmailHelper import okhttp3.HttpUrl -import okhttp3.HttpUrl.Companion.toHttpUrl -class AutoconfigUrlProvider { - fun getAutoconfigUrls(email: String): List { - val domain = EmailHelper.getDomainFromEmailAddress(email) - requireNotNull(domain) { "Couldn't extract domain from email address: $email" } - - return listOf( - createProviderUrl(domain, email), - createDomainUrl(scheme = "https", domain), - createDomainUrl(scheme = "http", domain), - createIspDbUrl(domain), - ) - } - - private fun createProviderUrl(domain: String?, email: String): HttpUrl { - // https://autoconfig.{domain}/mail/config-v1.1.xml?emailaddress={email} - return HttpUrl.Builder() - .scheme("https") - .host("autoconfig.$domain") - .addEncodedPathSegments("mail/config-v1.1.xml") - .addQueryParameter("emailaddress", email) - .build() - } - - private fun createDomainUrl(scheme: String, domain: String): HttpUrl { - // https://{domain}/.well-known/autoconfig/mail/config-v1.1.xml - // http://{domain}/.well-known/autoconfig/mail/config-v1.1.xml - return HttpUrl.Builder() - .scheme(scheme) - .host(domain) - .addEncodedPathSegments(".well-known/autoconfig/mail/config-v1.1.xml") - .build() - } - - private fun createIspDbUrl(domain: String): HttpUrl { - // https://autoconfig.thunderbird.net/v1.1/{domain} - return "https://autoconfig.thunderbird.net/v1.1/".toHttpUrl() - .newBuilder() - .addPathSegment(domain) - .build() - } +interface AutoconfigUrlProvider { + fun getAutoconfigUrls(email: String): List } diff --git a/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/IspDbAutoconfigUrlProvider.kt b/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/IspDbAutoconfigUrlProvider.kt new file mode 100644 index 000000000..e5ce650ce --- /dev/null +++ b/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/IspDbAutoconfigUrlProvider.kt @@ -0,0 +1,22 @@ +package app.k9mail.autodiscovery.autoconfig + +import com.fsck.k9.helper.EmailHelper +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl + +class IspDbAutoconfigUrlProvider : AutoconfigUrlProvider { + override fun getAutoconfigUrls(email: String): List { + val domain = EmailHelper.getDomainFromEmailAddress(email) + requireNotNull(domain) { "Couldn't extract domain from email address: $email" } + + return listOf(createIspDbUrl(domain)) + } + + private fun createIspDbUrl(domain: String): HttpUrl { + // https://autoconfig.thunderbird.net/v1.1/{domain} + return "https://autoconfig.thunderbird.net/v1.1/".toHttpUrl() + .newBuilder() + .addPathSegment(domain) + .build() + } +} diff --git a/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProvider.kt b/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProvider.kt new file mode 100644 index 000000000..22631dea8 --- /dev/null +++ b/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProvider.kt @@ -0,0 +1,37 @@ +package app.k9mail.autodiscovery.autoconfig + +import com.fsck.k9.helper.EmailHelper +import okhttp3.HttpUrl + +class ProviderAutoconfigUrlProvider : AutoconfigUrlProvider { + override fun getAutoconfigUrls(email: String): List { + val domain = EmailHelper.getDomainFromEmailAddress(email) + requireNotNull(domain) { "Couldn't extract domain from email address: $email" } + + return listOf( + createProviderUrl(domain, email), + createDomainUrl(scheme = "https", domain), + createDomainUrl(scheme = "http", domain), + ) + } + + private fun createProviderUrl(domain: String?, email: String): HttpUrl { + // https://autoconfig.{domain}/mail/config-v1.1.xml?emailaddress={email} + return HttpUrl.Builder() + .scheme("https") + .host("autoconfig.$domain") + .addEncodedPathSegments("mail/config-v1.1.xml") + .addQueryParameter("emailaddress", email) + .build() + } + + private fun createDomainUrl(scheme: String, domain: String): HttpUrl { + // https://{domain}/.well-known/autoconfig/mail/config-v1.1.xml + // http://{domain}/.well-known/autoconfig/mail/config-v1.1.xml + return HttpUrl.Builder() + .scheme(scheme) + .host(domain) + .addEncodedPathSegments(".well-known/autoconfig/mail/config-v1.1.xml") + .build() + } +} diff --git a/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/IspDbAutoconfigUrlProviderTest.kt b/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/IspDbAutoconfigUrlProviderTest.kt new file mode 100644 index 000000000..2bbb3f389 --- /dev/null +++ b/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/IspDbAutoconfigUrlProviderTest.kt @@ -0,0 +1,18 @@ +package app.k9mail.autodiscovery.autoconfig + +import assertk.assertThat +import assertk.assertions.containsExactly +import org.junit.Test + +class IspDbAutoconfigUrlProviderTest { + private val urlProvider = IspDbAutoconfigUrlProvider() + + @Test + fun `getAutoconfigUrls with ASCII email address`() { + val autoconfigUrls = urlProvider.getAutoconfigUrls("test@domain.example") + + assertThat(autoconfigUrls.map { it.toString() }).containsExactly( + "https://autoconfig.thunderbird.net/v1.1/domain.example", + ) + } +} diff --git a/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/AutoconfigUrlProviderTest.kt b/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProviderTest.kt similarity index 80% rename from feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/AutoconfigUrlProviderTest.kt rename to feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProviderTest.kt index 1d11956f8..bea8158fd 100644 --- a/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/AutoconfigUrlProviderTest.kt +++ b/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProviderTest.kt @@ -4,8 +4,8 @@ import assertk.assertThat import assertk.assertions.containsExactly import org.junit.Test -class AutoconfigUrlProviderTest { - private val urlProvider = AutoconfigUrlProvider() +class ProviderAutoconfigUrlProviderTest { + private val urlProvider = ProviderAutoconfigUrlProvider() @Test fun `getAutoconfigUrls with ASCII email address`() { @@ -15,7 +15,6 @@ class AutoconfigUrlProviderTest { "https://autoconfig.domain.example/mail/config-v1.1.xml?emailaddress=test%40domain.example", "https://domain.example/.well-known/autoconfig/mail/config-v1.1.xml", "http://domain.example/.well-known/autoconfig/mail/config-v1.1.xml", - "https://autoconfig.thunderbird.net/v1.1/domain.example", ) } } From 686bba8b9c78892b1dc0b7a3c061abd778a249bb Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 27 Apr 2023 17:05:42 +0200 Subject: [PATCH 2/3] Change `ProviderAutoconfigUrlProvider` to match Thunderbird's behavior See --- .../ProviderAutoconfigUrlProvider.kt | 22 +++++++++++-------- .../ProviderAutoconfigUrlProviderTest.kt | 5 +++-- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProvider.kt b/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProvider.kt index 22631dea8..c4b99ba58 100644 --- a/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProvider.kt +++ b/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProvider.kt @@ -9,29 +9,33 @@ class ProviderAutoconfigUrlProvider : AutoconfigUrlProvider { requireNotNull(domain) { "Couldn't extract domain from email address: $email" } return listOf( - createProviderUrl(domain, email), - createDomainUrl(scheme = "https", domain), - createDomainUrl(scheme = "http", domain), + createProviderUrl(domain, email, useHttps = true), + createDomainUrl(domain, email, useHttps = true), + + createProviderUrl(domain, email, useHttps = false), + createDomainUrl(domain, email, useHttps = false), ) } - private fun createProviderUrl(domain: String?, email: String): HttpUrl { + private fun createProviderUrl(domain: String, email: String, useHttps: Boolean): HttpUrl { // https://autoconfig.{domain}/mail/config-v1.1.xml?emailaddress={email} + // http://autoconfig.{domain}/mail/config-v1.1.xml?emailaddress={email} return HttpUrl.Builder() - .scheme("https") + .scheme(if (useHttps) "https" else "http") .host("autoconfig.$domain") .addEncodedPathSegments("mail/config-v1.1.xml") .addQueryParameter("emailaddress", email) .build() } - private fun createDomainUrl(scheme: String, domain: String): HttpUrl { - // https://{domain}/.well-known/autoconfig/mail/config-v1.1.xml - // http://{domain}/.well-known/autoconfig/mail/config-v1.1.xml + private fun createDomainUrl(domain: String, email: String, useHttps: Boolean): HttpUrl { + // https://{domain}/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress={email} + // http://{domain}/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress={email} return HttpUrl.Builder() - .scheme(scheme) + .scheme(if (useHttps) "https" else "http") .host(domain) .addEncodedPathSegments(".well-known/autoconfig/mail/config-v1.1.xml") + .addQueryParameter("emailaddress", email) .build() } } diff --git a/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProviderTest.kt b/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProviderTest.kt index bea8158fd..339c6954b 100644 --- a/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProviderTest.kt +++ b/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProviderTest.kt @@ -13,8 +13,9 @@ class ProviderAutoconfigUrlProviderTest { assertThat(autoconfigUrls.map { it.toString() }).containsExactly( "https://autoconfig.domain.example/mail/config-v1.1.xml?emailaddress=test%40domain.example", - "https://domain.example/.well-known/autoconfig/mail/config-v1.1.xml", - "http://domain.example/.well-known/autoconfig/mail/config-v1.1.xml", + "https://domain.example/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=test%40domain.example", + "http://autoconfig.domain.example/mail/config-v1.1.xml?emailaddress=test%40domain.example", + "http://domain.example/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=test%40domain.example", ) } } From 630b606f72395a3e06395b14105366ffd8c868fe Mon Sep 17 00:00:00 2001 From: cketti Date: Thu, 27 Apr 2023 17:20:53 +0200 Subject: [PATCH 3/3] Add config options to `ProviderAutoconfigUrlProvider` --- .../ProviderAutoconfigUrlProvider.kt | 33 +++++++++---- .../ProviderAutoconfigUrlProviderTest.kt | 48 +++++++++++++++++-- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProvider.kt b/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProvider.kt index c4b99ba58..1f4be940c 100644 --- a/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProvider.kt +++ b/feature/autodiscovery/autoconfig/src/main/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProvider.kt @@ -3,18 +3,20 @@ package app.k9mail.autodiscovery.autoconfig import com.fsck.k9.helper.EmailHelper import okhttp3.HttpUrl -class ProviderAutoconfigUrlProvider : AutoconfigUrlProvider { +class ProviderAutoconfigUrlProvider(private val config: AutoconfigUrlConfig) : AutoconfigUrlProvider { override fun getAutoconfigUrls(email: String): List { val domain = EmailHelper.getDomainFromEmailAddress(email) requireNotNull(domain) { "Couldn't extract domain from email address: $email" } - return listOf( - createProviderUrl(domain, email, useHttps = true), - createDomainUrl(domain, email, useHttps = true), + return buildList { + add(createProviderUrl(domain, email, useHttps = true)) + add(createDomainUrl(domain, email, useHttps = true)) - createProviderUrl(domain, email, useHttps = false), - createDomainUrl(domain, email, useHttps = false), - ) + if (!config.httpsOnly) { + add(createProviderUrl(domain, email, useHttps = false)) + add(createDomainUrl(domain, email, useHttps = false)) + } + } } private fun createProviderUrl(domain: String, email: String, useHttps: Boolean): HttpUrl { @@ -24,7 +26,11 @@ class ProviderAutoconfigUrlProvider : AutoconfigUrlProvider { .scheme(if (useHttps) "https" else "http") .host("autoconfig.$domain") .addEncodedPathSegments("mail/config-v1.1.xml") - .addQueryParameter("emailaddress", email) + .apply { + if (config.includeEmailAddress) { + addQueryParameter("emailaddress", email) + } + } .build() } @@ -35,7 +41,16 @@ class ProviderAutoconfigUrlProvider : AutoconfigUrlProvider { .scheme(if (useHttps) "https" else "http") .host(domain) .addEncodedPathSegments(".well-known/autoconfig/mail/config-v1.1.xml") - .addQueryParameter("emailaddress", email) + .apply { + if (config.includeEmailAddress) { + addQueryParameter("emailaddress", email) + } + } .build() } } + +data class AutoconfigUrlConfig( + val httpsOnly: Boolean, + val includeEmailAddress: Boolean, +) diff --git a/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProviderTest.kt b/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProviderTest.kt index 339c6954b..18607eca9 100644 --- a/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProviderTest.kt +++ b/feature/autodiscovery/autoconfig/src/test/kotlin/app/k9mail/autodiscovery/autoconfig/ProviderAutoconfigUrlProviderTest.kt @@ -5,10 +5,11 @@ import assertk.assertions.containsExactly import org.junit.Test class ProviderAutoconfigUrlProviderTest { - private val urlProvider = ProviderAutoconfigUrlProvider() - @Test - fun `getAutoconfigUrls with ASCII email address`() { + fun `getAutoconfigUrls with http allowed and email address included`() { + val urlProvider = ProviderAutoconfigUrlProvider( + AutoconfigUrlConfig(httpsOnly = false, includeEmailAddress = true), + ) val autoconfigUrls = urlProvider.getAutoconfigUrls("test@domain.example") assertThat(autoconfigUrls.map { it.toString() }).containsExactly( @@ -18,4 +19,45 @@ class ProviderAutoconfigUrlProviderTest { "http://domain.example/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=test%40domain.example", ) } + + @Test + fun `getAutoconfigUrls with only https and email address included`() { + val urlProvider = ProviderAutoconfigUrlProvider( + AutoconfigUrlConfig(httpsOnly = true, includeEmailAddress = true), + ) + val autoconfigUrls = urlProvider.getAutoconfigUrls("test@domain.example") + + assertThat(autoconfigUrls.map { it.toString() }).containsExactly( + "https://autoconfig.domain.example/mail/config-v1.1.xml?emailaddress=test%40domain.example", + "https://domain.example/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=test%40domain.example", + ) + } + + @Test + fun `getAutoconfigUrls with only https and email address not included`() { + val urlProvider = ProviderAutoconfigUrlProvider( + AutoconfigUrlConfig(httpsOnly = true, includeEmailAddress = false), + ) + val autoconfigUrls = urlProvider.getAutoconfigUrls("test@domain.example") + + assertThat(autoconfigUrls.map { it.toString() }).containsExactly( + "https://autoconfig.domain.example/mail/config-v1.1.xml", + "https://domain.example/.well-known/autoconfig/mail/config-v1.1.xml", + ) + } + + @Test + fun `getAutoconfigUrls with http allowed and email address not included`() { + val urlProvider = ProviderAutoconfigUrlProvider( + AutoconfigUrlConfig(httpsOnly = false, includeEmailAddress = false), + ) + val autoconfigUrls = urlProvider.getAutoconfigUrls("test@domain.example") + + assertThat(autoconfigUrls.map { it.toString() }).containsExactly( + "https://autoconfig.domain.example/mail/config-v1.1.xml", + "https://domain.example/.well-known/autoconfig/mail/config-v1.1.xml", + "http://autoconfig.domain.example/mail/config-v1.1.xml", + "http://domain.example/.well-known/autoconfig/mail/config-v1.1.xml", + ) + } }