Repo created
This commit is contained in:
parent
a629de6271
commit
3cef7c5092
2161 changed files with 246605 additions and 2 deletions
11
app/autodiscovery/srvrecords/build.gradle.kts
Normal file
11
app/autodiscovery/srvrecords/build.gradle.kts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
@Suppress("DSL_SCOPE_VIOLATION")
|
||||
plugins {
|
||||
id(ThunderbirdPlugins.Library.jvm)
|
||||
alias(libs.plugins.android.lint)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.app.autodiscovery.api)
|
||||
|
||||
implementation(libs.minidns.hla)
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.fsck.k9.autodiscovery.srvrecords
|
||||
|
||||
import com.fsck.k9.mail.ConnectionSecurity.SSL_TLS_REQUIRED
|
||||
import com.fsck.k9.mail.ConnectionSecurity.STARTTLS_REQUIRED
|
||||
import org.minidns.dnslabel.DnsLabel
|
||||
import org.minidns.dnsname.DnsName
|
||||
import org.minidns.hla.ResolverApi
|
||||
import org.minidns.hla.srv.SrvProto
|
||||
|
||||
class MiniDnsSrvResolver : SrvResolver {
|
||||
override fun lookup(domain: String, type: SrvType): List<MailService> {
|
||||
val result = ResolverApi.INSTANCE.resolveSrv(
|
||||
DnsLabel.from(type.label),
|
||||
SrvProto.tcp.dnsLabel,
|
||||
DnsName.from(domain)
|
||||
)
|
||||
|
||||
val security = if (type.assumeTls) SSL_TLS_REQUIRED else STARTTLS_REQUIRED
|
||||
return result.answersOrEmptySet.map {
|
||||
MailService(
|
||||
srvType = type,
|
||||
host = it.target.toString(),
|
||||
port = it.port,
|
||||
priority = it.priority,
|
||||
security = security
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.fsck.k9.autodiscovery.srvrecords
|
||||
|
||||
interface SrvResolver {
|
||||
fun lookup(domain: String, type: SrvType): List<MailService>
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package com.fsck.k9.autodiscovery.srvrecords
|
||||
|
||||
import com.fsck.k9.autodiscovery.api.ConnectionSettingsDiscovery
|
||||
import com.fsck.k9.autodiscovery.api.DiscoveredServerSettings
|
||||
import com.fsck.k9.autodiscovery.api.DiscoveryResults
|
||||
import com.fsck.k9.helper.EmailHelper
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import com.fsck.k9.mail.ConnectionSecurity
|
||||
|
||||
class SrvServiceDiscovery(
|
||||
private val srvResolver: MiniDnsSrvResolver
|
||||
) : ConnectionSettingsDiscovery {
|
||||
|
||||
override fun discover(email: String): DiscoveryResults? {
|
||||
val domain = EmailHelper.getDomainFromEmailAddress(email) ?: return null
|
||||
val mailServicePriority = compareBy<MailService> { it.priority }.thenByDescending { it.security }
|
||||
|
||||
val outgoingSettings = listOf(SrvType.SUBMISSIONS, SrvType.SUBMISSION)
|
||||
.flatMap { srvResolver.lookup(domain, it) }
|
||||
.sortedWith(mailServicePriority)
|
||||
.map { newServerSettings(it, email) }
|
||||
|
||||
val incomingSettings = listOf(SrvType.IMAPS, SrvType.IMAP)
|
||||
.flatMap { srvResolver.lookup(domain, it) }
|
||||
.sortedWith(mailServicePriority)
|
||||
.map { newServerSettings(it, email) }
|
||||
|
||||
return DiscoveryResults(incoming = incomingSettings, outgoing = outgoingSettings)
|
||||
}
|
||||
}
|
||||
|
||||
fun newServerSettings(service: MailService, email: String): DiscoveredServerSettings {
|
||||
return DiscoveredServerSettings(
|
||||
service.srvType.protocol,
|
||||
service.host,
|
||||
service.port,
|
||||
service.security,
|
||||
AuthType.PLAIN,
|
||||
email
|
||||
)
|
||||
}
|
||||
|
||||
enum class SrvType(val label: String, val protocol: String, val assumeTls: Boolean) {
|
||||
SUBMISSIONS("_submissions", "smtp", true),
|
||||
SUBMISSION("_submission", "smtp", false),
|
||||
IMAPS("_imaps", "imap", true),
|
||||
IMAP("_imap", "imap", false)
|
||||
}
|
||||
|
||||
data class MailService(
|
||||
val srvType: SrvType,
|
||||
val host: String,
|
||||
val port: Int,
|
||||
val priority: Int,
|
||||
val security: ConnectionSecurity
|
||||
)
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
package com.fsck.k9.autodiscovery.srvrecords
|
||||
|
||||
import com.fsck.k9.autodiscovery.api.DiscoveryResults
|
||||
import com.fsck.k9.mail.ConnectionSecurity
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
|
||||
class SrvServiceDiscoveryTest {
|
||||
|
||||
@Test
|
||||
fun discover_whenNoMailServices_shouldReturnNoResults() {
|
||||
val srvResolver = newMockSrvResolver()
|
||||
|
||||
val srvServiceDiscovery = SrvServiceDiscovery(srvResolver)
|
||||
val result = srvServiceDiscovery.discover("test@example.com")
|
||||
|
||||
assertEquals(DiscoveryResults(listOf(), listOf()), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun discover_whenNoSMTP_shouldReturnJustIMAP() {
|
||||
val srvResolver = newMockSrvResolver(
|
||||
imapServices = listOf(newMailService(port = 143, srvType = SrvType.IMAP)),
|
||||
imapsServices = listOf(
|
||||
newMailService(port = 993, srvType = SrvType.IMAPS, security = ConnectionSecurity.SSL_TLS_REQUIRED)
|
||||
)
|
||||
)
|
||||
|
||||
val srvServiceDiscovery = SrvServiceDiscovery(srvResolver)
|
||||
val result = srvServiceDiscovery.discover("test@example.com")
|
||||
|
||||
assertEquals(2, result!!.incoming.size)
|
||||
assertEquals(0, result.outgoing.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun discover_whenNoIMAP_shouldReturnJustSMTP() {
|
||||
val srvResolver = newMockSrvResolver(
|
||||
submissionServices = listOf(
|
||||
newMailService(
|
||||
port = 25,
|
||||
srvType = SrvType.SUBMISSION,
|
||||
security = ConnectionSecurity.STARTTLS_REQUIRED
|
||||
),
|
||||
newMailService(
|
||||
port = 465,
|
||||
srvType = SrvType.SUBMISSIONS,
|
||||
security = ConnectionSecurity.SSL_TLS_REQUIRED
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val srvServiceDiscovery = SrvServiceDiscovery(srvResolver)
|
||||
val result = srvServiceDiscovery.discover("test@example.com")
|
||||
|
||||
assertEquals(0, result!!.incoming.size)
|
||||
assertEquals(2, result.outgoing.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun discover_withRequiredServices_shouldCorrectlyPrioritize() {
|
||||
val srvResolver = newMockSrvResolver(
|
||||
submissionServices = listOf(
|
||||
newMailService(
|
||||
host = "smtp1.example.com",
|
||||
port = 25,
|
||||
srvType = SrvType.SUBMISSION,
|
||||
security = ConnectionSecurity.STARTTLS_REQUIRED,
|
||||
priority = 0
|
||||
),
|
||||
newMailService(
|
||||
host = "smtp2.example.com",
|
||||
port = 25,
|
||||
srvType = SrvType.SUBMISSION,
|
||||
security = ConnectionSecurity.STARTTLS_REQUIRED,
|
||||
priority = 1
|
||||
)
|
||||
),
|
||||
submissionsServices = listOf(
|
||||
newMailService(
|
||||
host = "smtp3.example.com",
|
||||
port = 465,
|
||||
srvType = SrvType.SUBMISSIONS,
|
||||
security = ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
priority = 0
|
||||
),
|
||||
newMailService(
|
||||
host = "smtp4.example.com",
|
||||
port = 465,
|
||||
srvType = SrvType.SUBMISSIONS,
|
||||
security = ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
priority = 1
|
||||
)
|
||||
),
|
||||
imapServices = listOf(
|
||||
newMailService(
|
||||
host = "imap1.example.com",
|
||||
port = 143,
|
||||
srvType = SrvType.IMAP,
|
||||
security = ConnectionSecurity.STARTTLS_REQUIRED,
|
||||
priority = 0
|
||||
),
|
||||
newMailService(
|
||||
host = "imap2.example.com",
|
||||
port = 143,
|
||||
srvType = SrvType.IMAP,
|
||||
security = ConnectionSecurity.STARTTLS_REQUIRED,
|
||||
priority = 1
|
||||
)
|
||||
),
|
||||
imapsServices = listOf(
|
||||
newMailService(
|
||||
host = "imaps1.example.com",
|
||||
port = 993,
|
||||
srvType = SrvType.IMAPS,
|
||||
security = ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
priority = 0
|
||||
),
|
||||
newMailService(
|
||||
host = "imaps2.example.com",
|
||||
port = 993,
|
||||
srvType = SrvType.IMAPS,
|
||||
security = ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
priority = 1
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val srvServiceDiscovery = SrvServiceDiscovery(srvResolver)
|
||||
val result = srvServiceDiscovery.discover("test@example.com")
|
||||
|
||||
assertEquals(
|
||||
listOf(
|
||||
"smtp3.example.com",
|
||||
"smtp1.example.com",
|
||||
"smtp4.example.com",
|
||||
"smtp2.example.com"
|
||||
),
|
||||
result?.outgoing?.map { it.host }
|
||||
)
|
||||
assertEquals(
|
||||
listOf(
|
||||
"imaps1.example.com",
|
||||
"imap1.example.com",
|
||||
"imaps2.example.com",
|
||||
"imap2.example.com"
|
||||
),
|
||||
result?.incoming?.map { it.host }
|
||||
)
|
||||
}
|
||||
|
||||
private fun newMailService(
|
||||
host: String = "example.com",
|
||||
priority: Int = 0,
|
||||
security: ConnectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED,
|
||||
srvType: SrvType,
|
||||
port: Int
|
||||
): MailService {
|
||||
return MailService(srvType, host, port, priority, security)
|
||||
}
|
||||
|
||||
private fun newMockSrvResolver(
|
||||
host: String = "example.com",
|
||||
submissionServices: List<MailService> = listOf(),
|
||||
submissionsServices: List<MailService> = listOf(),
|
||||
imapServices: List<MailService> = listOf(),
|
||||
imapsServices: List<MailService> = listOf()
|
||||
): MiniDnsSrvResolver {
|
||||
return mock {
|
||||
on { lookup(host, SrvType.SUBMISSION) } doReturn submissionServices
|
||||
on { lookup(host, SrvType.SUBMISSIONS) } doReturn submissionsServices
|
||||
on { lookup(host, SrvType.IMAP) } doReturn imapServices
|
||||
on { lookup(host, SrvType.IMAPS) } doReturn imapsServices
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue