Clean up code

This commit is contained in:
cketti 2019-03-21 23:52:57 +01:00
parent b7652205ae
commit d6423ff85e
4 changed files with 161 additions and 133 deletions

View file

@ -1,26 +1,40 @@
package com.fsck.k9.autodiscovery.thunderbird
import com.fsck.k9.helper.EmailHelper
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.InputStream
import java.net.URLEncoder
class ThunderbirdAutoconfigFetcher(private val client: OkHttpClient) {
class ThunderbirdAutoconfigFetcher(private val okHttpClient: OkHttpClient) {
fun fetchAutoconfigFile(email: String): InputStream? {
val url = getAutodiscoveryAddress(email)
val request = Request.Builder().url(url).build()
return client.newCall(request).execute().body()?.byteStream()
val response = okHttpClient.newCall(request).execute()
return if (response.isSuccessful) {
response.body()?.byteStream()
} else {
null
}
}
companion object {
// address described at:
// https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration#Configuration_server_at_ISP
internal fun getAutodiscoveryAddress(email: String): String {
internal fun getAutodiscoveryAddress(email: String): HttpUrl {
val domain = EmailHelper.getDomainFromEmailAddress(email)
return "https://${domain}/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=" + URLEncoder.encode(email, "UTF-8")
requireNotNull(domain) { "Couldn't extract domain from email address: $email" }
return HttpUrl.Builder()
.scheme("https")
.host(domain)
.addEncodedPathSegments(".well-known/autoconfig/mail/config-v1.1.xml")
.addQueryParameter("emailaddress", email)
.build()
}
}
}

View file

@ -11,14 +11,12 @@ import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
/**
* Parser for Thunderbird's
* [Autoconfig file format](https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat)
*/
class ThunderbirdAutoconfigParser {
// structure described at:
// https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat
fun parseSettings(stream: InputStream?, email: String): ConnectionSettings? {
if (stream == null) {
return null
}
fun parseSettings(stream: InputStream, email: String): ConnectionSettings? {
var incoming: ServerSettings? = null
var outgoing: ServerSettings? = null
@ -30,10 +28,13 @@ class ThunderbirdAutoconfigParser {
var eventType = xpp.eventType
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if ("incomingServer" == xpp.name) {
incoming = parseServer(xpp, "incomingServer", email)
} else if ("outgoingServer" == xpp.name) {
outgoing = parseServer(xpp, "outgoingServer", email)
when (xpp.name) {
"incomingServer" -> {
incoming = parseServer(xpp, "incomingServer", email)
}
"outgoingServer" -> {
outgoing = parseServer(xpp, "outgoingServer", email)
}
}
if (incoming != null && outgoing != null) {
@ -56,16 +57,22 @@ class ThunderbirdAutoconfigParser {
var eventType = xpp.eventType
while (!(eventType == XmlPullParser.END_TAG && nodeName == xpp.name)) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.name == "hostname") {
host = getText(xpp)
} else if (xpp.name == "port") {
port = getText(xpp).toInt()
} else if (xpp.name == "username") {
username = getText(xpp).replace("%EMAILADDRESS%", email)
} else if (xpp.name == "authentication" && authType == null) {
authType = parseAuthType(getText(xpp))
} else if (xpp.name == "socketType") {
connectionSecurity = parseSocketType(getText(xpp))
when (xpp.name) {
"hostname" -> {
host = getText(xpp)
}
"port" -> {
port = getText(xpp).toInt()
}
"username" -> {
username = getText(xpp).replace("%EMAILADDRESS%", email)
}
"authentication" -> {
if (authType == null) authType = parseAuthType(getText(xpp))
}
"socketType" -> {
connectionSecurity = parseSocketType(getText(xpp))
}
}
}
eventType = xpp.next()
@ -95,8 +102,6 @@ class ThunderbirdAutoconfigParser {
@Throws(XmlPullParserException::class, IOException::class)
private fun getText(xpp: XmlPullParser): String {
val eventType = xpp.next()
return if (eventType != XmlPullParser.TEXT) {
""
} else xpp.text
return if (eventType != XmlPullParser.TEXT) "" else xpp.text
}
}

View file

@ -9,7 +9,9 @@ class ThunderbirdDiscovery(
): ConnectionSettingsDiscovery {
override fun discover(email: String): ConnectionSettings? {
return fetcher.fetchAutoconfigFile(email).use {
val autoconfigInputStream = fetcher.fetchAutoconfigFile(email) ?: return null
return autoconfigInputStream.use {
parser.parseSettings(it, email)
}
}

View file

@ -1,9 +1,7 @@
package com.fsck.k9.autodiscovery.thunderbird
import com.fsck.k9.RobolectricTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import com.google.common.truth.Truth.assertThat
import org.junit.Test
class ThunderbirdAutoconfigTest : RobolectricTest() {
@ -11,133 +9,142 @@ class ThunderbirdAutoconfigTest : RobolectricTest() {
@Test
fun settingsExtract() {
val settings = parser.parseSettings("""<?xml version="1.0"?>
<clientConfig version="1.1">
<emailProvider id="metacode.biz">
<domain>metacode.biz</domain>
val input = """<?xml version="1.0"?>
<clientConfig version="1.1">
<emailProvider id="metacode.biz">
<domain>metacode.biz</domain>
<incomingServer type="imap">
<hostname>imap.googlemail.com</hostname>
<port>993</port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>OAuth2</authentication>
<authentication>password-cleartext</authentication>
</incomingServer>
<incomingServer type="imap">
<hostname>imap.googlemail.com</hostname>
<port>993</port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>OAuth2</authentication>
<authentication>password-cleartext</authentication>
</incomingServer>
<outgoingServer type="smtp">
<hostname>smtp.googlemail.com</hostname>
<port>465</port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>OAuth2</authentication>
<authentication>password-cleartext</authentication>
<addThisServer>true</addThisServer>
</outgoingServer>
</emailProvider>
<outgoingServer type="smtp">
<hostname>smtp.googlemail.com</hostname>
<port>465</port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>OAuth2</authentication>
<authentication>password-cleartext</authentication>
<addThisServer>true</addThisServer>
</outgoingServer>
</emailProvider>
</clientConfig>
""".trimIndent().byteInputStream()
</clientConfig>""".byteInputStream(), "test@metacode.biz")
val connectionSettings = parser.parseSettings(input, "test@metacode.biz")
assertNotNull(settings)
assertNotNull(settings?.outgoing)
assertNotNull(settings?.incoming)
assertEquals("imap.googlemail.com", settings?.incoming?.host)
assertEquals(993, settings?.incoming?.port)
assertEquals("test@metacode.biz", settings?.incoming?.username)
assertEquals("smtp.googlemail.com", settings?.outgoing?.host)
assertEquals(465, settings?.outgoing?.port)
assertEquals("test@metacode.biz", settings?.outgoing?.username)
assertThat(connectionSettings).isNotNull()
assertThat(connectionSettings!!.incoming).isNotNull()
assertThat(connectionSettings.outgoing).isNotNull()
with(connectionSettings.incoming) {
assertThat(host).isEqualTo("imap.googlemail.com")
assertThat(port).isEqualTo(993)
assertThat(username).isEqualTo("test@metacode.biz")
}
with(connectionSettings.outgoing) {
assertThat(host).isEqualTo("smtp.googlemail.com")
assertThat(port).isEqualTo(465)
assertThat(username).isEqualTo("test@metacode.biz")
}
}
@Test
fun pickFirstServer() {
val settings = parser.parseSettings("""<?xml version="1.0"?>
<clientConfig version="1.1">
<emailProvider id="metacode.biz">
<domain>metacode.biz</domain>
val input = """<?xml version="1.0"?>
<clientConfig version="1.1">
<emailProvider id="metacode.biz">
<domain>metacode.biz</domain>
<incomingServer type="imap">
<hostname>imap.googlemail.com</hostname>
<port>993</port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>OAuth2</authentication>
<authentication>password-cleartext</authentication>
</incomingServer>
<incomingServer type="imap">
<hostname>imap.googlemail.com</hostname>
<port>993</port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>OAuth2</authentication>
<authentication>password-cleartext</authentication>
</incomingServer>
<outgoingServer type="smtp">
<hostname>first</hostname>
<port>465</port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>OAuth2</authentication>
<authentication>password-cleartext</authentication>
<addThisServer>true</addThisServer>
</outgoingServer>
<outgoingServer type="smtp">
<hostname>first</hostname>
<port>465</port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>OAuth2</authentication>
<authentication>password-cleartext</authentication>
<addThisServer>true</addThisServer>
</outgoingServer>
<outgoingServer type="smtp">
<hostname>second</hostname>
<port>465</port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>OAuth2</authentication>
<authentication>password-cleartext</authentication>
<addThisServer>true</addThisServer>
</outgoingServer>
</emailProvider>
<outgoingServer type="smtp">
<hostname>second</hostname>
<port>465</port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>OAuth2</authentication>
<authentication>password-cleartext</authentication>
<addThisServer>true</addThisServer>
</outgoingServer>
</emailProvider>
</clientConfig>
""".trimIndent().byteInputStream()
</clientConfig>""".byteInputStream(), "test@metacode.biz")
val connectionSettings = parser.parseSettings(input, "test@metacode.biz")
assertNotNull(settings)
assertNotNull(settings?.outgoing)
assertNotNull(settings?.incoming)
assertEquals("first", settings?.outgoing?.host)
assertEquals(465, settings?.outgoing?.port)
assertEquals("test@metacode.biz", settings?.outgoing?.username)
assertThat(connectionSettings).isNotNull()
assertThat(connectionSettings!!.outgoing).isNotNull()
with(connectionSettings.outgoing) {
assertThat(host).isEqualTo("first")
assertThat(port).isEqualTo(465)
assertThat(username).isEqualTo("test@metacode.biz")
}
}
@Test
fun invalidResponse() {
val settings = parser.parseSettings("""<?xml version="1.0"?>
<clientConfig version="1.1">
<emailProvider id="metacode.biz">
<domain>metacode.biz</domain>""".byteInputStream(), "test@metacode.biz")
val input = """<?xml version="1.0"?>
<clientConfig version="1.1">
<emailProvider id="metacode.biz">
<domain>metacode.biz</domain>
""".trimIndent().byteInputStream()
assertNull(settings)
val connectionSettings = parser.parseSettings(input, "test@metacode.biz")
assertThat(connectionSettings).isNull()
}
@Test
fun notCompleteConfiguration() {
val settings = parser.parseSettings("""<?xml version="1.0"?>
<clientConfig version="1.1">
<emailProvider id="metacode.biz">
<domain>metacode.biz</domain>
fun incompleteConfiguration() {
val input = """<?xml version="1.0"?>
<clientConfig version="1.1">
<emailProvider id="metacode.biz">
<domain>metacode.biz</domain>
<incomingServer type="imap">
<hostname>imap.googlemail.com</hostname>
<port>993</port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>OAuth2</authentication>
<authentication>password-cleartext</authentication>
</incomingServer>
</emailProvider>
</clientConfig>""".byteInputStream(), "test@metacode.biz")
<incomingServer type="imap">
<hostname>imap.googlemail.com</hostname>
<port>993</port>
<socketType>SSL</socketType>
<username>%EMAILADDRESS%</username>
<authentication>OAuth2</authentication>
<authentication>password-cleartext</authentication>
</incomingServer>
</emailProvider>
</clientConfig>
""".trimIndent().byteInputStream()
assertNull(settings)
val connectionSettings = parser.parseSettings(input, "test@metacode.biz")
assertThat(connectionSettings).isNull()
}
@Test
fun generatedUrls() {
val email = "test@metacode.biz"
val autoDiscoveryAddress = ThunderbirdAutoconfigFetcher.getAutodiscoveryAddress("test@metacode.biz")
val actual = ThunderbirdAutoconfigFetcher.getAutodiscoveryAddress(email)
val expected = "https://metacode.biz/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=test%40metacode.biz"
assertEquals(expected, actual)
assertThat(autoDiscoveryAddress.toString()).isEqualTo("https://metacode.biz/" +
".well-known/autoconfig/mail/config-v1.1.xml?emailaddress=test%40metacode.biz")
}
}