Use EmailAddress
class in :feature:autodiscovery:autoconfig module
This commit is contained in:
parent
57e3db5443
commit
3bb0d2e529
9 changed files with 59 additions and 49 deletions
|
@ -6,3 +6,5 @@ value class EmailAddress(val address: String) {
|
|||
require(address.isNotBlank()) { "Email address must not be blank" }
|
||||
}
|
||||
}
|
||||
|
||||
fun String.toEmailAddress() = EmailAddress(this)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package app.k9mail.autodiscovery.autoconfig
|
||||
|
||||
import app.k9mail.autodiscovery.api.ConnectionSettingsDiscovery
|
||||
import app.k9mail.autodiscovery.api.DiscoveryResults
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import app.k9mail.core.common.net.toDomain
|
||||
import com.fsck.k9.helper.EmailHelper
|
||||
|
||||
|
@ -9,10 +9,10 @@ class AutoconfigDiscovery(
|
|||
private val urlProvider: AutoconfigUrlProvider,
|
||||
private val fetcher: AutoconfigFetcher,
|
||||
private val parser: AutoconfigParser,
|
||||
) : ConnectionSettingsDiscovery {
|
||||
) {
|
||||
|
||||
override fun discover(email: String): DiscoveryResults? {
|
||||
val domain = requireNotNull(EmailHelper.getDomainFromEmailAddress(email)?.toDomain()) {
|
||||
fun discover(email: EmailAddress): DiscoveryResults? {
|
||||
val domain = requireNotNull(EmailHelper.getDomainFromEmailAddress(email.address)?.toDomain()) {
|
||||
"Couldn't extract domain from email address: $email"
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package app.k9mail.autodiscovery.autoconfig
|
|||
|
||||
import app.k9mail.autodiscovery.api.DiscoveredServerSettings
|
||||
import app.k9mail.autodiscovery.api.DiscoveryResults
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import app.k9mail.core.common.net.HostNameUtils
|
||||
import com.fsck.k9.helper.EmailHelper
|
||||
import com.fsck.k9.logging.Timber
|
||||
|
@ -19,9 +20,9 @@ import org.xmlpull.v1.XmlPullParserFactory
|
|||
* See [https://github.com/thundernest/autoconfig](https://github.com/thundernest/autoconfig)
|
||||
*/
|
||||
class AutoconfigParser {
|
||||
fun parseSettings(stream: InputStream, email: String): DiscoveryResults {
|
||||
fun parseSettings(stream: InputStream, email: EmailAddress): DiscoveryResults {
|
||||
return try {
|
||||
ClientConfigParser(stream, email).parse()
|
||||
ClientConfigParser(stream, email.address).parse()
|
||||
} catch (e: XmlPullParserException) {
|
||||
throw AutoconfigParserException("Error parsing Autoconfig XML", e)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package app.k9mail.autodiscovery.autoconfig
|
||||
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import app.k9mail.core.common.net.Domain
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
interface AutoconfigUrlProvider {
|
||||
fun getAutoconfigUrls(domain: Domain, email: String? = null): List<HttpUrl>
|
||||
fun getAutoconfigUrls(domain: Domain, email: EmailAddress? = null): List<HttpUrl>
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package app.k9mail.autodiscovery.autoconfig
|
||||
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import app.k9mail.core.common.net.Domain
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
|
||||
class IspDbAutoconfigUrlProvider : AutoconfigUrlProvider {
|
||||
override fun getAutoconfigUrls(domain: Domain, email: String?): List<HttpUrl> {
|
||||
override fun getAutoconfigUrls(domain: Domain, email: EmailAddress?): List<HttpUrl> {
|
||||
return listOf(createIspDbUrl(domain))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package app.k9mail.autodiscovery.autoconfig
|
||||
|
||||
import app.k9mail.autodiscovery.api.ConnectionSettingsDiscovery
|
||||
import app.k9mail.autodiscovery.api.DiscoveryResults
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import app.k9mail.core.common.net.Domain
|
||||
import app.k9mail.core.common.net.toDomain
|
||||
import com.fsck.k9.helper.EmailHelper
|
||||
|
@ -13,12 +13,12 @@ class MxLookupAutoconfigDiscovery(
|
|||
private val urlProvider: AutoconfigUrlProvider,
|
||||
private val fetcher: AutoconfigFetcher,
|
||||
private val parser: AutoconfigParser,
|
||||
) : ConnectionSettingsDiscovery {
|
||||
) {
|
||||
|
||||
@Suppress("ReturnCount")
|
||||
override fun discover(email: String): DiscoveryResults? {
|
||||
val domain = requireNotNull(EmailHelper.getDomainFromEmailAddress(email)?.toDomain()) {
|
||||
"Couldn't extract domain from email address: $email"
|
||||
fun discover(email: EmailAddress): DiscoveryResults? {
|
||||
val domain = requireNotNull(EmailHelper.getDomainFromEmailAddress(email.address)?.toDomain()) {
|
||||
"Couldn't extract domain from email address: ${email.address}"
|
||||
}
|
||||
|
||||
val mxHostName = mxLookup(domain) ?: return null
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package app.k9mail.autodiscovery.autoconfig
|
||||
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import app.k9mail.core.common.net.Domain
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
class ProviderAutoconfigUrlProvider(private val config: AutoconfigUrlConfig) : AutoconfigUrlProvider {
|
||||
override fun getAutoconfigUrls(domain: Domain, email: String?): List<HttpUrl> {
|
||||
override fun getAutoconfigUrls(domain: Domain, email: EmailAddress?): List<HttpUrl> {
|
||||
return buildList {
|
||||
add(createProviderUrl(domain, email, useHttps = true))
|
||||
add(createDomainUrl(domain, email, useHttps = true))
|
||||
|
@ -16,7 +17,7 @@ class ProviderAutoconfigUrlProvider(private val config: AutoconfigUrlConfig) : A
|
|||
}
|
||||
}
|
||||
|
||||
private fun createProviderUrl(domain: Domain, email: String?, useHttps: Boolean): HttpUrl {
|
||||
private fun createProviderUrl(domain: Domain, email: EmailAddress?, 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()
|
||||
|
@ -25,13 +26,13 @@ class ProviderAutoconfigUrlProvider(private val config: AutoconfigUrlConfig) : A
|
|||
.addEncodedPathSegments("mail/config-v1.1.xml")
|
||||
.apply {
|
||||
if (email != null && config.includeEmailAddress) {
|
||||
addQueryParameter("emailaddress", email)
|
||||
addQueryParameter("emailaddress", email.address)
|
||||
}
|
||||
}
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun createDomainUrl(domain: Domain, email: String?, useHttps: Boolean): HttpUrl {
|
||||
private fun createDomainUrl(domain: Domain, email: EmailAddress?, 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()
|
||||
|
@ -40,7 +41,7 @@ class ProviderAutoconfigUrlProvider(private val config: AutoconfigUrlConfig) : A
|
|||
.addEncodedPathSegments(".well-known/autoconfig/mail/config-v1.1.xml")
|
||||
.apply {
|
||||
if (email != null && config.includeEmailAddress) {
|
||||
addQueryParameter("emailaddress", email)
|
||||
addQueryParameter("emailaddress", email.address)
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
|
|
@ -2,6 +2,7 @@ package app.k9mail.autodiscovery.autoconfig
|
|||
|
||||
import app.k9mail.autodiscovery.api.DiscoveredServerSettings
|
||||
import app.k9mail.autodiscovery.api.DiscoveryResults
|
||||
import app.k9mail.core.common.mail.toEmailAddress
|
||||
import assertk.all
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
|
@ -50,11 +51,13 @@ class AutoconfigParserTest {
|
|||
</clientConfig>
|
||||
""".trimIndent()
|
||||
|
||||
private val irrelevantEmailAddress = "irrelevant@domain.example".toEmailAddress()
|
||||
|
||||
@Test
|
||||
fun `minimal data`() {
|
||||
val inputStream = minimalConfig.byteInputStream()
|
||||
|
||||
val result = parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
val result = parser.parseSettings(inputStream, email = "user@domain.example".toEmailAddress())
|
||||
|
||||
assertThat(result).isNotNull().all {
|
||||
prop(DiscoveryResults::incoming).containsExactly(
|
||||
|
@ -84,7 +87,7 @@ class AutoconfigParserTest {
|
|||
fun `real-world data`() {
|
||||
val inputStream = javaClass.getResourceAsStream("/2022-11-19-googlemail.com.xml")!!
|
||||
|
||||
val result = parser.parseSettings(inputStream, email = "test@gmail.com")
|
||||
val result = parser.parseSettings(inputStream, email = "test@gmail.com".toEmailAddress())
|
||||
|
||||
assertThat(result).isNotNull().all {
|
||||
prop(DiscoveryResults::incoming).containsExactly(
|
||||
|
@ -118,7 +121,7 @@ class AutoconfigParserTest {
|
|||
element("outgoingServer > username").text("%EMAILDOMAIN%")
|
||||
}
|
||||
|
||||
val result = parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
val result = parser.parseSettings(inputStream, email = "user@domain.example".toEmailAddress())
|
||||
|
||||
assertThat(result).isNotNull().all {
|
||||
prop(DiscoveryResults::incoming).containsExactly(
|
||||
|
@ -154,7 +157,7 @@ class AutoconfigParserTest {
|
|||
element("incomingServer > username").prepend("<!-- comment -->")
|
||||
}
|
||||
|
||||
val result = parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
val result = parser.parseSettings(inputStream, email = "user@domain.example".toEmailAddress())
|
||||
|
||||
assertThat(result.incoming).containsExactly(
|
||||
DiscoveredServerSettings(
|
||||
|
@ -174,7 +177,7 @@ class AutoconfigParserTest {
|
|||
element("incomingServer").insertBefore("""<incomingServer type="smtp"/>""")
|
||||
}
|
||||
|
||||
val result = parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
val result = parser.parseSettings(inputStream, email = "user@domain.example".toEmailAddress())
|
||||
|
||||
assertThat(result.incoming).containsExactly(
|
||||
DiscoveredServerSettings(
|
||||
|
@ -194,7 +197,7 @@ class AutoconfigParserTest {
|
|||
element("outgoingServer").insertBefore("""<outgoingServer type="imap"/>""")
|
||||
}
|
||||
|
||||
val result = parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
val result = parser.parseSettings(inputStream, email = "user@domain.example".toEmailAddress())
|
||||
|
||||
assertThat(result.outgoing).containsExactly(
|
||||
DiscoveredServerSettings(
|
||||
|
@ -214,7 +217,7 @@ class AutoconfigParserTest {
|
|||
element("incomingServer > authentication").insertBefore("<authentication></authentication>")
|
||||
}
|
||||
|
||||
val result = parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
val result = parser.parseSettings(inputStream, email = "user@domain.example".toEmailAddress())
|
||||
|
||||
assertThat(result.incoming).containsExactly(
|
||||
DiscoveredServerSettings(
|
||||
|
@ -235,7 +238,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Missing 'emailProvider.id' attribute")
|
||||
}
|
||||
|
@ -247,7 +250,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Invalid 'emailProvider.id' attribute")
|
||||
}
|
||||
|
@ -259,7 +262,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Valid 'domain' element required")
|
||||
}
|
||||
|
@ -271,7 +274,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Valid 'domain' element required")
|
||||
}
|
||||
|
@ -283,7 +286,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Missing 'incomingServer' element")
|
||||
}
|
||||
|
@ -295,7 +298,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Missing 'outgoingServer' element")
|
||||
}
|
||||
|
@ -307,7 +310,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Missing 'hostname' element")
|
||||
}
|
||||
|
@ -319,7 +322,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Invalid 'hostname' value: 'in valid'")
|
||||
}
|
||||
|
@ -331,7 +334,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Missing 'port' element")
|
||||
}
|
||||
|
@ -343,7 +346,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Missing 'socketType' element")
|
||||
}
|
||||
|
@ -355,7 +358,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("No usable 'authentication' element found")
|
||||
}
|
||||
|
@ -367,7 +370,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Missing 'username' element")
|
||||
}
|
||||
|
@ -379,7 +382,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Invalid 'port' value: 'invalid'")
|
||||
}
|
||||
|
@ -391,7 +394,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Invalid 'port' value: '100000'")
|
||||
}
|
||||
|
@ -403,7 +406,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Unknown 'socketType' value: 'TLS'")
|
||||
}
|
||||
|
@ -415,7 +418,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Expected text, but got START_TAG")
|
||||
}
|
||||
|
@ -427,7 +430,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Missing 'emailProvider' element")
|
||||
}
|
||||
|
@ -442,7 +445,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Missing 'incomingServer' element")
|
||||
}
|
||||
|
@ -457,7 +460,7 @@ class AutoconfigParserTest {
|
|||
}
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "user@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Missing 'outgoingServer' element")
|
||||
}
|
||||
|
@ -467,7 +470,7 @@ class AutoconfigParserTest {
|
|||
val inputStream = "invalid".byteInputStream()
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "irrelevant@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Error parsing Autoconfig XML")
|
||||
}
|
||||
|
@ -482,7 +485,7 @@ class AutoconfigParserTest {
|
|||
""".trimIndent().byteInputStream()
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "irrelevant@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Missing 'clientConfig' element")
|
||||
}
|
||||
|
@ -514,7 +517,7 @@ class AutoconfigParserTest {
|
|||
""".trimIndent().byteInputStream()
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "irrelevant@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("Error parsing Autoconfig XML")
|
||||
}
|
||||
|
@ -530,7 +533,7 @@ class AutoconfigParserTest {
|
|||
""".trimIndent().byteInputStream()
|
||||
|
||||
assertFailure {
|
||||
parser.parseSettings(inputStream, email = "irrelevant@domain.example")
|
||||
parser.parseSettings(inputStream, irrelevantEmailAddress)
|
||||
}.isInstanceOf<AutoconfigParserException>()
|
||||
.hasMessage("End of document reached while reading element 'emailProvider'")
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package app.k9mail.autodiscovery.autoconfig
|
||||
|
||||
import app.k9mail.core.common.mail.toEmailAddress
|
||||
import app.k9mail.core.common.net.toDomain
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.containsExactly
|
||||
|
@ -7,7 +8,7 @@ import org.junit.Test
|
|||
|
||||
class ProviderAutoconfigUrlProviderTest {
|
||||
private val domain = "domain.example".toDomain()
|
||||
private val email = "test@domain.example"
|
||||
private val email = "test@domain.example".toEmailAddress()
|
||||
|
||||
@Test
|
||||
fun `getAutoconfigUrls with http allowed and email address included`() {
|
||||
|
|
Loading…
Reference in a new issue