-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
ProvidersXmlDiscovery.kt
156 lines (131 loc) · 6.16 KB
/
ProvidersXmlDiscovery.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package com.fsck.k9.autodiscovery.providersxml
import android.content.res.XmlResourceParser
import android.net.Uri
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
import com.fsck.k9.oauth.OAuthConfigurationProvider
import com.fsck.k9.preferences.Protocols
import org.xmlpull.v1.XmlPullParser
import timber.log.Timber
class ProvidersXmlDiscovery(
private val xmlProvider: ProvidersXmlProvider,
private val oAuthConfigurationProvider: OAuthConfigurationProvider,
) : ConnectionSettingsDiscovery {
override fun discover(email: String): DiscoveryResults? {
val domain = EmailHelper.getDomainFromEmailAddress(email) ?: return null
val provider = findProviderForDomain(domain) ?: return null
val incomingSettings = provider.toIncomingServerSettings(email) ?: return null
val outgoingSettings = provider.toOutgoingServerSettings(email) ?: return null
return DiscoveryResults(listOf(incomingSettings), listOf(outgoingSettings))
}
private fun findProviderForDomain(domain: String): Provider? {
return try {
xmlProvider.getXml().use { xml ->
parseProviders(xml, domain)
}
} catch (e: Exception) {
Timber.e(e, "Error while trying to load provider settings.")
null
}
}
private fun parseProviders(xml: XmlResourceParser, domain: String): Provider? {
do {
val xmlEventType = xml.next()
if (xmlEventType == XmlPullParser.START_TAG && xml.name == "provider") {
val providerDomain = xml.getAttributeValue(null, "domain")
if (domain.equals(providerDomain, ignoreCase = true)) {
val provider = parseProvider(xml)
if (provider != null) return provider
}
}
} while (xmlEventType != XmlPullParser.END_DOCUMENT)
return null
}
private fun parseProvider(xml: XmlResourceParser): Provider? {
var incomingUriTemplate: String? = null
var incomingUsernameTemplate: String? = null
var outgoingUriTemplate: String? = null
var outgoingUsernameTemplate: String? = null
do {
val xmlEventType = xml.next()
if (xmlEventType == XmlPullParser.START_TAG) {
when (xml.name) {
"incoming" -> {
incomingUriTemplate = xml.getAttributeValue(null, "uri")
incomingUsernameTemplate = xml.getAttributeValue(null, "username")
}
"outgoing" -> {
outgoingUriTemplate = xml.getAttributeValue(null, "uri")
outgoingUsernameTemplate = xml.getAttributeValue(null, "username")
}
}
}
} while (!(xmlEventType == XmlPullParser.END_TAG && xml.name == "provider"))
return if (incomingUriTemplate != null && incomingUsernameTemplate != null && outgoingUriTemplate != null &&
outgoingUsernameTemplate != null
) {
Provider(incomingUriTemplate, incomingUsernameTemplate, outgoingUriTemplate, outgoingUsernameTemplate)
} else {
null
}
}
private fun Provider.toIncomingServerSettings(email: String): DiscoveredServerSettings? {
val user = EmailHelper.getLocalPartFromEmailAddress(email) ?: return null
val domain = EmailHelper.getDomainFromEmailAddress(email) ?: return null
val username = incomingUsernameTemplate.fillInUsernameTemplate(email, user, domain)
val security = when {
incomingUriTemplate.startsWith("imap+ssl") -> ConnectionSecurity.SSL_TLS_REQUIRED
incomingUriTemplate.startsWith("imap+tls") -> ConnectionSecurity.STARTTLS_REQUIRED
else -> error("Connection security required")
}
val uri = Uri.parse(incomingUriTemplate)
val host = uri.host ?: error("Host name required")
val port = if (uri.port == -1) {
if (security == ConnectionSecurity.STARTTLS_REQUIRED) 143 else 993
} else {
uri.port
}
val authType = if (oAuthConfigurationProvider.getConfiguration(host) != null) {
AuthType.XOAUTH2
} else {
AuthType.PLAIN
}
return DiscoveredServerSettings(Protocols.IMAP, host, port, security, authType, username)
}
private fun Provider.toOutgoingServerSettings(email: String): DiscoveredServerSettings? {
val user = EmailHelper.getLocalPartFromEmailAddress(email) ?: return null
val domain = EmailHelper.getDomainFromEmailAddress(email) ?: return null
val username = outgoingUsernameTemplate.fillInUsernameTemplate(email, user, domain)
val security = when {
outgoingUriTemplate.startsWith("smtp+ssl") -> ConnectionSecurity.SSL_TLS_REQUIRED
outgoingUriTemplate.startsWith("smtp+tls") -> ConnectionSecurity.STARTTLS_REQUIRED
else -> error("Connection security required")
}
val uri = Uri.parse(outgoingUriTemplate)
val host = uri.host ?: error("Host name required")
val port = if (uri.port == -1) {
if (security == ConnectionSecurity.STARTTLS_REQUIRED) 587 else 465
} else {
uri.port
}
val authType = if (oAuthConfigurationProvider.getConfiguration(host) != null) {
AuthType.XOAUTH2
} else {
AuthType.PLAIN
}
return DiscoveredServerSettings(Protocols.SMTP, host, port, security, authType, username)
}
private fun String.fillInUsernameTemplate(email: String, user: String, domain: String): String {
return this.replace("\$email", email).replace("\$user", user).replace("\$domain", domain)
}
internal data class Provider(
val incomingUriTemplate: String,
val incomingUsernameTemplate: String,
val outgoingUriTemplate: String,
val outgoingUsernameTemplate: String,
)
}