Repo created
This commit is contained in:
parent
a629de6271
commit
3cef7c5092
2161 changed files with 246605 additions and 2 deletions
9
app/autodiscovery/api/build.gradle.kts
Normal file
9
app/autodiscovery/api/build.gradle.kts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
@Suppress("DSL_SCOPE_VIOLATION")
|
||||
plugins {
|
||||
id(ThunderbirdPlugins.Library.jvm)
|
||||
alias(libs.plugins.android.lint)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.mail.common)
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.fsck.k9.autodiscovery.api
|
||||
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import com.fsck.k9.mail.ConnectionSecurity
|
||||
|
||||
interface ConnectionSettingsDiscovery {
|
||||
fun discover(email: String): DiscoveryResults?
|
||||
}
|
||||
|
||||
data class DiscoveryResults(val incoming: List<DiscoveredServerSettings>, val outgoing: List<DiscoveredServerSettings>)
|
||||
|
||||
data class DiscoveredServerSettings(
|
||||
val protocol: String,
|
||||
val host: String,
|
||||
val port: Int,
|
||||
val security: ConnectionSecurity,
|
||||
val authType: AuthType?,
|
||||
val username: String?
|
||||
)
|
||||
20
app/autodiscovery/providersxml/build.gradle.kts
Normal file
20
app/autodiscovery/providersxml/build.gradle.kts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
plugins {
|
||||
id(ThunderbirdPlugins.Library.android)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.app.core)
|
||||
implementation(projects.mail.common)
|
||||
implementation(projects.app.autodiscovery.api)
|
||||
|
||||
implementation(libs.timber)
|
||||
|
||||
testImplementation(projects.app.testing)
|
||||
testImplementation(projects.backend.imap)
|
||||
testImplementation(libs.robolectric)
|
||||
testImplementation(libs.androidx.test.core)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.fsck.k9.autodiscovery.providersxml"
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.fsck.k9.autodiscovery.providersxml
|
||||
|
||||
import org.koin.dsl.module
|
||||
|
||||
val autodiscoveryProvidersXmlModule = module {
|
||||
factory { ProvidersXmlProvider(context = get()) }
|
||||
factory { ProvidersXmlDiscovery(xmlProvider = get(), oAuthConfigurationProvider = get()) }
|
||||
}
|
||||
|
|
@ -0,0 +1,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
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.fsck.k9.autodiscovery.providersxml
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.XmlResourceParser
|
||||
|
||||
class ProvidersXmlProvider(private val context: Context) {
|
||||
fun getXml(): XmlResourceParser {
|
||||
return context.resources.getXml(R.xml.providers)
|
||||
}
|
||||
}
|
||||
774
app/autodiscovery/providersxml/src/main/res/xml/providers.xml
Normal file
774
app/autodiscovery/providersxml/src/main/res/xml/providers.xml
Normal file
|
|
@ -0,0 +1,774 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!--
|
||||
This file is used to specify providers that we know default settings for
|
||||
so that the user can set up their account by simply entering their email
|
||||
address and password.
|
||||
|
||||
When a user starts this process, the email address is parsed, the domain
|
||||
broken out and used to search this file for a provider. If one is found the
|
||||
provider's settings are used to attempt to connect to the account.
|
||||
|
||||
At this time, the id and label attributes are not used. However, please include them
|
||||
if you make edits to this file. id must also be completely unique. label will be shown
|
||||
to the user when there are multiple options provided for a single domain (not currently
|
||||
supported).
|
||||
|
||||
A provider contains the settings for setting up an email account
|
||||
that ends with the given domain. Domains should be unique within
|
||||
this file. Each provider should have at least one incoming section and
|
||||
one outgoing section. If more than one is specified only the first
|
||||
will be used.
|
||||
|
||||
Valid incoming uri schemes are:
|
||||
imap+tls+ IMAP with required TLS transport security.
|
||||
If TLS is not available the connection fails.
|
||||
imap+ssl+ IMAP with required SSL transport security.
|
||||
If SSL is not available the connection fails.
|
||||
|
||||
Valid outgoing uri schemes are:
|
||||
smtp+tls+ SMTP with required TLS transport security.
|
||||
If TLS is not available the connection fails.
|
||||
smtp+ssl+ SMTP with required SSL transport security.
|
||||
If SSL is not available the connection fails.
|
||||
|
||||
The URIs should be full templates for connection, including a port if
|
||||
the service uses a non-default port. The default ports are as follows:
|
||||
imap+tls+ 143 smtp+tls+ 587
|
||||
imap+ssl+ 993 smtp+ssl+ 465
|
||||
|
||||
The username attribute is used to supply a template for the username
|
||||
that will be presented to the server. This username is built from a
|
||||
set of variables that are substituted with parts of the user
|
||||
specified email address.
|
||||
|
||||
Valid substitution values for the username attribute are:
|
||||
$email - the email address the user entered
|
||||
$user - the value before the @ sign in the email address the user entered
|
||||
$domain - the value after the @ sign in the email address the user entered
|
||||
|
||||
The username attribute MUST be specified for the incoming element, so the IMAP
|
||||
server can identify the mailbox to be opened.
|
||||
|
||||
The username attribute MAY be the empty string for the outgoing element, but only if the
|
||||
SMTP server supports anonymous transmission (most don't).
|
||||
|
||||
While it would technically work please DO NOT add providers that don't support encrypted
|
||||
connections.
|
||||
-->
|
||||
|
||||
<providers>
|
||||
|
||||
<!-- Gmail variants -->
|
||||
<provider id="gmail" label="Gmail" domain="gmail.com">
|
||||
<incoming uri="imap+ssl+://imap.gmail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.gmail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="googlemail" label="Google Mail" domain="googlemail.com">
|
||||
<incoming uri="imap+ssl+://imap.googlemail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.googlemail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="google" label="Google" domain="google.com">
|
||||
<incoming uri="imap+ssl+://imap.gmail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.gmail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="android" label="Android" domain="android.com">
|
||||
<incoming uri="imap+ssl+://imap.gmail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.gmail.com" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- USA -->
|
||||
<provider id="comcast" label="Comcast" domain="comcast.net">
|
||||
<incoming uri="imap+ssl+://imap.comcast.net" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.comcast.net" username="$email" />
|
||||
</provider>
|
||||
<provider id="montclair.edu" label="MSU" domain="montclair.edu">
|
||||
<incoming uri="imap+ssl+://mail.montclair.edu" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.montclair.edu" username="$user" />
|
||||
</provider>
|
||||
<provider id="gmx.com" label="GMX" domain="gmx.com">
|
||||
<incoming uri="imap+ssl+://imap.gmx.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://mail.gmx.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="zoho.com" label="Zoho Mail" domain="zoho.com">
|
||||
<incoming uri="imap+ssl+://imap.zoho.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.zoho.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="riseup" label="Riseup Networks" domain="riseup.net">
|
||||
<incoming uri="imap+ssl+://mail.riseup.net" username="$user" />
|
||||
<outgoing uri="smtp+tls+://mail.riseup.net" username="$user" />
|
||||
</provider>
|
||||
|
||||
<!-- Mail.com Variants -->
|
||||
<provider id="mail.com" label="Mail.com" domain="mail.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="email.com" label="Mail.com" domain="email.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="techie.com" label="Mail.com" domain="techie.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="email.com" label="Mail.com" domain="email.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="usa.com" label="Mail.com" domain="usa.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="myself.com" label="Mail.com" domain="myself.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="consultant.com" label="Mail.com" domain="consultant.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="post.com" label="Mail.com" domain="post.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="europe.com" label="Mail.com" domain="europe.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="asia.com" label="Mail.com" domain="asia.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="iname.com" label="Mail.com" domain="iname.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="writeme.com" label="Mail.com" domain="writeme.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="dr.com" label="Mail.com" domain="dr.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="engineer.com" label="Mail.com" domain="engineer.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="cheerful.com" label="Mail.com" domain="cheerful.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="accountant.com" label="Mail.com" domain="accountant.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="techie.com" label="Mail.com" domain="techie.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="linuxmail.org" label="Mail.com" domain="linuxmail.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="uymail.com" label="Mail.com" domain="uymail.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="contractor.net" label="Mail.com" domain="contractor.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.com" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- Yahoo! Mail Variants -->
|
||||
<provider id="yahoo" label="Yahoo" domain="yahoo.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.yahoo.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.yahoo.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="yahoo.de" label="Yahoo" domain="yahoo.de">
|
||||
<incoming uri="imap+ssl+://imap.mail.yahoo.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.yahoo.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="ymail" label="YMail" domain="ymail.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.yahoo.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.yahoo.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="rocketmail" label="Rocketmail" domain="rocketmail.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.yahoo.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.yahoo.com" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- Apple -->
|
||||
<provider id="apple" label="Apple" domain="apple.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.apple.com" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.mail.apple.com" username="$user" />
|
||||
</provider>
|
||||
<provider id="dotmac" label=".Mac" domain="mac.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.mac.com" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.mail.mac.com" username="$user" />
|
||||
</provider>
|
||||
<provider id="mobileme" label="MobileMe" domain="me.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.me.com" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.mail.me.com" username="$user" />
|
||||
</provider>
|
||||
<provider id="icloud" label="iCloud" domain="icloud.com">
|
||||
<incoming uri="imap+ssl+://imap.mail.icloud.com" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.mail.icloud.com" username="$user" />
|
||||
</provider>
|
||||
|
||||
<!-- Australia -->
|
||||
<provider id="fastmail-fm" label="Fastmail" domain="fastmail.fm">
|
||||
<incoming uri="imap+ssl+://mail.messagingengine.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://mail.messagingengine.com" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- Virgin Media variants -->
|
||||
<provider id="virginmedia.com" label="Virgin Media" domain="virginmedia.com">
|
||||
<incoming uri="imap+ssl+://imap.virginmedia.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.virginmedia.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="virgin.net" label="Virgin Media" domain="virgin.net">
|
||||
<incoming uri="imap+ssl+://imap.virginmedia.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.virginmedia.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="blueyonder.co.uk" label="Virgin Media" domain="blueyonder.co.uk">
|
||||
<incoming uri="imap+ssl+://imap.virginmedia.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.virginmedia.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="ntlworld.com" label="Virgin Media" domain="ntlworld.com">
|
||||
<incoming uri="imap+ssl+://imap.virginmedia.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.virginmedia.com" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- France -->
|
||||
<provider id="mailo.com" label="mailo.com" domain="mailo.com">
|
||||
<incoming uri="imap+ssl+://mail.mailo.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://mail.mailo.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="net-c.fr" label="net-c.fr" domain="net-c.fr">
|
||||
<incoming uri="imap+ssl+://mail.mailo.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://mail.mailo.com" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- Germany -->
|
||||
<provider id="mailbox.org" label="mailbox.org" domain="mailbox.org">
|
||||
<incoming uri="imap+tls+://imap.mailbox.org" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.mailbox.org" username="$email" />
|
||||
</provider>
|
||||
<provider id="freenet" label="Freenet" domain="freenet.de">
|
||||
<incoming uri="imap+tls+://mx.freenet.de" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mx.freenet.de" username="$email" />
|
||||
</provider>
|
||||
<provider id="T-Online" label="T-Online" domain="t-online.de">
|
||||
<incoming uri="imap+ssl+://secureimap.t-online.de" username="$email" />
|
||||
<outgoing uri="smtp+tls+://securesmtp.t-online.de" username="$email" />
|
||||
</provider>
|
||||
<provider id="web.de" label="Web.de" domain="web.de">
|
||||
<incoming uri="imap+ssl+://imap.web.de" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.web.de" username="$user" />
|
||||
</provider>
|
||||
<provider id="posteo" label="Posteo" domain="posteo.net">
|
||||
<incoming uri="imap+tls+://posteo.de" username="$email" />
|
||||
<outgoing uri="smtp+tls+://posteo.de" username="$email" />
|
||||
</provider>
|
||||
<provider id="posteo" label="Posteo" domain="posteo.de">
|
||||
<incoming uri="imap+tls+://posteo.de" username="$email" />
|
||||
<outgoing uri="smtp+tls+://posteo.de" username="$email" />
|
||||
</provider>
|
||||
<provider id="systemliorg" label="systemli.org" domain="systemli.org">
|
||||
<incoming uri="imap+ssl+://mail.systemli.org" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mail.systemli.org" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- GMX variants -->
|
||||
<provider id="gmx.net" label="GMX.net" domain="gmx.net">
|
||||
<incoming uri="imap+ssl+://imap.gmx.net" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mail.gmx.net" username="$email" />
|
||||
</provider>
|
||||
<provider id="gmx.de" label="GMX.de" domain="gmx.de">
|
||||
<incoming uri="imap+ssl+://imap.gmx.net" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mail.gmx.net" username="$email" />
|
||||
</provider>
|
||||
<provider id="gmx.at" label="GMX.at" domain="gmx.at">
|
||||
<incoming uri="imap+ssl+://imap.gmx.net" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mail.gmx.net" username="$email" />
|
||||
</provider>
|
||||
<provider id="gmx.ch" label="GMX.ch" domain="gmx.ch">
|
||||
<incoming uri="imap+ssl+://imap.gmx.net" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mail.gmx.net" username="$email" />
|
||||
</provider>
|
||||
<provider id="gmx.eu" label="GMX.eu" domain="gmx.eu">
|
||||
<incoming uri="imap+ssl+://imap.gmx.net" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mail.gmx.net" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- Greece -->
|
||||
<provider id="otenet.gr" label="otenet.gr" domain="otenet.gr">
|
||||
<incoming uri="imap+ssl+://imap.otenet.gr" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mailgate.otenet.gr" username="$email" />
|
||||
</provider>
|
||||
<provider id="cosmotemail.gr" label="cosmotemail" domain="cosmotemail.gr">
|
||||
<incoming uri="imap+ssl+://imap.cosmotemail.gr" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mailgate.cosmotemail.gr" username="$email" />
|
||||
</provider>
|
||||
<provider id="mycosmos.gr" label="mycosmos" domain="mycosmos.gr">
|
||||
<incoming uri="imap+ssl+://mail.mycosmos.gr" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mail.mycosmos.gr" username="$email" />
|
||||
</provider>
|
||||
<provider id="espiv" label="Espiv.net" domain="espiv.net">
|
||||
<incoming uri="imap+ssl+://mail.espiv.net" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mail.espiv.net" username="$email" />
|
||||
</provider>
|
||||
<provider id="squat" label="Squat.gr" domain="squat.gr">
|
||||
<incoming uri="imap+ssl+://mail.espiv.net" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mail.espiv.net" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- Italy -->
|
||||
<provider id="poste" label="poste" domain="poste.it">
|
||||
<incoming uri="imap+ssl+://relay.poste.it" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://relay.poste.it" username="$email" />
|
||||
</provider>
|
||||
<provider id="vodafone" label="vodafone" domain="vodafone.it">
|
||||
<incoming uri="imap+ssl+://imap.vodafone.it" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.vodafone.it" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- Switzerland -->
|
||||
<!-- KolabNow.com variants -->
|
||||
<provider id="kolabnow.com" label="KolabNow.com" domain="kolabnow.com">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="attorneymail.ch" label="KolabNow.com" domain="attorneymail.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="barmail.ch" label="KolabNow.com" domain="barmail.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="collaborative.li" label="KolabNow.com" domain="collaborative.li">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="diplomail.ch" label="KolabNow.com" domain="diplomail.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="groupoffice.ch" label="KolabNow.com" domain="groupoffice.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="journalistmail.ch" label="KolabNow.com" domain="journalistmail.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="legalprivilege.ch" label="KolabNow.com" domain="legalprivilege.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="libertymail.co" label="KolabNow.com" domain="libertymail.co">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="libertymail.net" label="KolabNow.com" domain="libertymail.net">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="mailatlaw.ch" label="KolabNow.com" domain="mailatlaw.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="medmail.ch" label="KolabNow.com" domain="medmail.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="mykolab.ch" label="KolabNow.com" domain="mykolab.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="mykolab.com" label="KolabNow.com" domain="mykolab.com">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="myswissmail.ch" label="KolabNow.com" domain="myswissmail.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="opengroupware.ch" label="KolabNow.com" domain="opengroupware.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="pressmail.ch" label="KolabNow.com" domain="pressmail.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="swisscollab.ch" label="KolabNow.com" domain="swisscollab.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="swissgroupware.ch" label="KolabNow.com" domain="swissgroupware.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="switzerlandmail.ch" label="KolabNow.com" domain="switzerlandmail.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="trusted-legal-mail.ch" label="KolabNow.com" domain="trusted-legal-mail.ch">
|
||||
<incoming uri="imap+ssl+://imap.kolabnow.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.kolabnow.com" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- Japanese -->
|
||||
<provider id="auone" label="au one" domain="auone.jp">
|
||||
<incoming uri="imap+ssl+://imap.gmail.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.gmail.com" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- Korean -->
|
||||
<provider id="naver" label="Naver" domain="naver.com">
|
||||
<incoming uri="imap+ssl+://imap.naver.com" username="$user" />
|
||||
<outgoing uri="smtp+tls+://smtp.naver.com:587" username="$user" />
|
||||
</provider>
|
||||
<provider id="hanmail" label="Hanmail" domain="hanmail.net">
|
||||
<incoming uri="imap+ssl+://imap.hanmail.net" username="$user" />
|
||||
<outgoing uri="smtp+ssl+://smtp.hanmail.net" username="$user" />
|
||||
</provider>
|
||||
<provider id="daum" label="Hanmail" domain="daum.net">
|
||||
<incoming uri="imap+ssl+://imap.hanmail.net" username="$user" />
|
||||
<outgoing uri="smtp+ssl+://smtp.hanmail.net" username="$user" />
|
||||
</provider>
|
||||
|
||||
<!-- Russia -->
|
||||
<!-- Mail.Ru variants -->
|
||||
<provider id="rumailmailimap" label="mail.ru" domain="mail.ru">
|
||||
<incoming uri="imap+ssl+://imap.mail.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="rumailinboximap" label="inbox.ru" domain="inbox.ru">
|
||||
<incoming uri="imap+ssl+://imap.mail.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="rumaillistimap" label="list.ru" domain="list.ru">
|
||||
<incoming uri="imap+ssl+://imap.mail.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="rumailbkimap" label="bk.ru" domain="bk.ru">
|
||||
<incoming uri="imap+ssl+://imap.mail.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail.ru" username="$email" />
|
||||
</provider>
|
||||
<!-- Yandex variants -->
|
||||
<provider id="comyanyandeximap" label="yandex.com" domain="yandex.com">
|
||||
<incoming uri="imap+ssl+://imap.yandex.com" username="$user" />
|
||||
<outgoing uri="smtp+ssl+://smtp.yandex.com" username="$user" />
|
||||
</provider>
|
||||
<provider id="ruyanyandeximap" label="yandex.ru" domain="yandex.ru">
|
||||
<incoming uri="imap+ssl+://imap.yandex.ru" username="$user" />
|
||||
<outgoing uri="smtp+ssl+://smtp.yandex.ru" username="$user" />
|
||||
</provider>
|
||||
<provider id="ruyanyaimap" label="ya.ru" domain="ya.ru">
|
||||
<incoming uri="imap+ssl+://imap.ya.ru" username="$user" />
|
||||
<outgoing uri="smtp+ssl+://smtp.ya.ru" username="$user" />
|
||||
</provider>
|
||||
<provider id="byyandeximap" label="yandex.by" domain="yandex.by">
|
||||
<incoming uri="imap+ssl+://imap.yandex.by" username="$user" />
|
||||
<outgoing uri="smtp+ssl+://smtp.yandex.by" username="$user" />
|
||||
</provider>
|
||||
<provider id="kzyandeximap" label="yandex.kz" domain="yandex.kz">
|
||||
<incoming uri="imap+ssl+://imap.yandex.kz" username="$user" />
|
||||
<outgoing uri="smtp+ssl+://smtp.yandex.kz" username="$user" />
|
||||
</provider>
|
||||
<provider id="uayandeximap" label="yandex.ua" domain="yandex.ua">
|
||||
<incoming uri="imap+ssl+://imap.yandex.ua" username="$user" />
|
||||
<outgoing uri="smtp+ssl+://smtp.yandex.ua" username="$user" />
|
||||
</provider>
|
||||
<!-- Rambler.ru variants -->
|
||||
<provider id="ruramramblerimap" label="rambler.ru" domain="rambler.ru">
|
||||
<incoming uri="imap+ssl+://mail.rambler.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://mail.rambler.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruramlentaimap" label="lenta.ru" domain="lenta.ru">
|
||||
<incoming uri="imap+ssl+://mail.rambler.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://mail.rambler.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruramroimap" label="ro.ru" domain="ro.ru">
|
||||
<incoming uri="imap+ssl+://mail.rambler.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://mail.rambler.ru" username="$email" />
|
||||
</provider>
|
||||
<!-- QIP.RU variants -->
|
||||
<provider id="ruqipqipimap" label="qip.ru" domain="qip.ru">
|
||||
<incoming uri="imap+ssl+://imap.qip.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.qip.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqippochtaimap" label="pochta.ru" domain="pochta.ru">
|
||||
<incoming uri="imap+ssl+://imap.pochta.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.pochta.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="comqipfromruimap" label="fromru.com" domain="fromru.com">
|
||||
<incoming uri="imap+ssl+://imap.fromru.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.fromru.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqipfrontimap" label="front.ru" domain="front.ru">
|
||||
<incoming uri="imap+ssl+://imap.front.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.front.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqiphotboximap" label="hotbox.ru" domain="hotbox.ru">
|
||||
<incoming uri="imap+ssl+://imap.hotbox.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.hotbox.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqiphotmailimap" label="hotmail.ru" domain="hotmail.ru">
|
||||
<incoming uri="imap+ssl+://imap.hotmail.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.hotmail.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="suqipkrovatkaimap" label="krovatka.su" domain="krovatka.su">
|
||||
<incoming uri="imap+ssl+://imap.krovatka.su" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.krovatka.su" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqiplandimap" label="land.ru" domain="land.ru">
|
||||
<incoming uri="imap+ssl+://imap.land.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.land.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="comqipmail15imap" label="mail15.com" domain="mail15.com">
|
||||
<incoming uri="imap+ssl+://imap.mail15.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail15.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="comqipmail333imap" label="mail333.com" domain="mail333.com">
|
||||
<incoming uri="imap+ssl+://imap.mail333.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.mail333.com" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqipnewmailimap" label="newmail.ru" domain="newmail.ru">
|
||||
<incoming uri="imap+ssl+://imap.newmail.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.newmail.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqipnightmailimap" label="nightmail.ru" domain="nightmail.ru">
|
||||
<incoming uri="imap+ssl+://imap.nightmail.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.nightmail.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqipnmimap" label="nm.ru" domain="nm.ru">
|
||||
<incoming uri="imap+ssl+://imap.nm.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.nm.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="netqippisemimap" label="pisem.net" domain="pisem.net">
|
||||
<incoming uri="imap+ssl+://imap.pisem.net" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.pisem.net" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqippochtamtimap" label="pochtamt.ru" domain="pochtamt.ru">
|
||||
<incoming uri="imap+ssl+://imap.pochtamt.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.pochtamt.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqippop3imap" label="pop3.ru" domain="pop3.ru">
|
||||
<incoming uri="imap+ssl+://imap.pop3.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.pop3.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqiprbcmailimap" label="rbcmail.ru" domain="rbcmail.ru">
|
||||
<incoming uri="imap+ssl+://imap.rbcmail.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.rbcmail.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqipsmtpimap" label="smtp.ru" domain="smtp.ru">
|
||||
<incoming uri="imap+ssl+://imap.smtp.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.smtp.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqip5ballovimap" label="5ballov.ru" domain="5ballov.ru">
|
||||
<incoming uri="imap+ssl+://imap.5ballov.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.5ballov.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqipaeternaimap" label="aeterna.ru" domain="aeterna.ru">
|
||||
<incoming uri="imap+ssl+://imap.aeterna.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aeterna.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqipzizaimap" label="ziza.ru" domain="ziza.ru">
|
||||
<incoming uri="imap+ssl+://imap.ziza.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.ziza.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqipmemoriimap" label="memori.ru" domain="memori.ru">
|
||||
<incoming uri="imap+ssl+://imap.memori.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.memori.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqipphotofileimap" label="photofile.ru" domain="photofile.ru">
|
||||
<incoming uri="imap+ssl+://imap.photofile.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.photofile.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="ruqipfotoplenkaimap" label="fotoplenka.ru" domain="fotoplenka.ru">
|
||||
<incoming uri="imap+ssl+://imap.fotoplenka.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.fotoplenka.ru" username="$email" />
|
||||
</provider>
|
||||
<provider id="comqippochtaimap" label="pochta.com" domain="pochta.com">
|
||||
<incoming uri="imap+ssl+://imap.pochta.ru" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.pochta.ru" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- Slovakia -->
|
||||
<provider id="azet.sk" label="Azet.sk" domain="azet.sk">
|
||||
<incoming uri="imap+ssl+://imap.azet.sk" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.azet.sk" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- The Netherlands -->
|
||||
<!-- Ziggo variants -->
|
||||
<provider id="casema.nl" label="Ziggo" domain="casema.nl">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="chello.nl" label="Ziggo" domain="chello.nl">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="hahah.nl" label="Ziggo" domain="hahah.nl">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="home.nl" label="Ziggo" domain="home.nl">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="multiweb.nl" label="Ziggo" domain="multiweb.nl">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="quicknet.nl" label="Ziggo" domain="quicknet.nl">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="razcall.com" label="Ziggo" domain="razcall.com">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="razcall.nl" label="Ziggo" domain="razcall.nl">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="upcmail.nl" label="Ziggo" domain="upcmail.nl">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="zeggis.com" label="Ziggo" domain="zeggis.com">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="zeggis.nl" label="Ziggo" domain="zeggis.nl">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="ziggomail.com" label="Ziggo" domain="ziggomail.com">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="ziggo.nl" label="Ziggo" domain="ziggo.nl">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
<provider id="zinders.nl" label="Ziggo" domain="zinders.nl">
|
||||
<incoming uri="imap+ssl+://imap.ziggo.nl" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.ziggo.nl" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- EU wide -->
|
||||
<provider id="fairnatics.net" label="Fairnatics" domain="fairnatics.net">
|
||||
<incoming uri="imap+ssl+://mail.fairnatics.net" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mail.fairnatics.net:25" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- eFoundation -->
|
||||
<provider id="e.foundation" label="/e/" domain="e.email">
|
||||
<incoming uri="imap+ssl+://mail.ecloud.global" username="$email" />
|
||||
<outgoing uri="smtp+tls+://mail.ecloud.global" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- AOL variants -->
|
||||
<provider domain="aol.com">
|
||||
<incoming uri="imap+ssl+://imap.aol.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aol.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="aol.de">
|
||||
<incoming uri="imap+ssl+://imap.aol.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aol.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="aol.it">
|
||||
<incoming uri="imap+ssl+://imap.aol.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aol.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="aol.fr">
|
||||
<incoming uri="imap+ssl+://imap.aol.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aol.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="aol.es">
|
||||
<incoming uri="imap+ssl+://imap.aol.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aol.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="aol.se">
|
||||
<incoming uri="imap+ssl+://imap.aol.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aol.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="aol.co.uk">
|
||||
<incoming uri="imap+ssl+://imap.aol.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aol.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="aol.co.nz">
|
||||
<incoming uri="imap+ssl+://imap.aol.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aol.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="aol.com.au">
|
||||
<incoming uri="imap+ssl+://imap.aol.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aol.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="aol.com.ar">
|
||||
<incoming uri="imap+ssl+://imap.aol.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aol.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="aol.com.br">
|
||||
<incoming uri="imap+ssl+://imap.aol.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aol.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="aol.com.mx">
|
||||
<incoming uri="imap+ssl+://imap.aol.com" username="$email" />
|
||||
<outgoing uri="smtp+ssl+://smtp.aol.com" username="$email" />
|
||||
</provider>
|
||||
|
||||
<!-- Microsoft variants -->
|
||||
<provider domain="outlook.com">
|
||||
<incoming uri="imap+ssl+://outlook.office365.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.office365.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="hotmail.com">
|
||||
<incoming uri="imap+ssl+://outlook.office365.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.office365.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="msn.com">
|
||||
<incoming uri="imap+ssl+://outlook.office365.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.office365.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="live.com">
|
||||
<incoming uri="imap+ssl+://outlook.office365.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.office365.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="live.co.uk">
|
||||
<incoming uri="imap+ssl+://outlook.office365.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.office365.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="hotmail.co.uk">
|
||||
<incoming uri="imap+ssl+://outlook.office365.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.office365.com" username="$email" />
|
||||
</provider>
|
||||
<provider domain="outlook.sk">
|
||||
<incoming uri="imap+ssl+://outlook.office365.com" username="$email" />
|
||||
<outgoing uri="smtp+tls+://smtp.office365.com" username="$email" />
|
||||
</provider>
|
||||
|
||||
</providers>
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package com.fsck.k9.autodiscovery.providersxml
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isNotNull
|
||||
import assertk.assertions.isNull
|
||||
import com.fsck.k9.RobolectricTest
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import com.fsck.k9.mail.ConnectionSecurity
|
||||
import com.fsck.k9.oauth.OAuthConfiguration
|
||||
import com.fsck.k9.oauth.OAuthConfigurationProvider
|
||||
import org.junit.Test
|
||||
|
||||
class ProvidersXmlDiscoveryTest : RobolectricTest() {
|
||||
private val xmlProvider = ProvidersXmlProvider(ApplicationProvider.getApplicationContext())
|
||||
private val oAuthConfigurationProvider = createOAuthConfigurationProvider()
|
||||
private val providersXmlDiscovery = ProvidersXmlDiscovery(xmlProvider, oAuthConfigurationProvider)
|
||||
|
||||
@Test
|
||||
fun discover_withGmailDomain_shouldReturnCorrectSettings() {
|
||||
val connectionSettings = providersXmlDiscovery.discover("user@gmail.com")
|
||||
|
||||
assertThat(connectionSettings).isNotNull()
|
||||
with(connectionSettings!!.incoming.first()) {
|
||||
assertThat(host).isEqualTo("imap.gmail.com")
|
||||
assertThat(security).isEqualTo(ConnectionSecurity.SSL_TLS_REQUIRED)
|
||||
assertThat(authType).isEqualTo(AuthType.XOAUTH2)
|
||||
assertThat(username).isEqualTo("user@gmail.com")
|
||||
}
|
||||
with(connectionSettings.outgoing.first()) {
|
||||
assertThat(host).isEqualTo("smtp.gmail.com")
|
||||
assertThat(security).isEqualTo(ConnectionSecurity.SSL_TLS_REQUIRED)
|
||||
assertThat(authType).isEqualTo(AuthType.XOAUTH2)
|
||||
assertThat(username).isEqualTo("user@gmail.com")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun discover_withUnknownDomain_shouldReturnNull() {
|
||||
val connectionSettings = providersXmlDiscovery.discover(
|
||||
"user@not.present.in.providers.xml.example"
|
||||
)
|
||||
|
||||
assertThat(connectionSettings).isNull()
|
||||
}
|
||||
|
||||
private fun createOAuthConfigurationProvider(): OAuthConfigurationProvider {
|
||||
val googleConfig = OAuthConfiguration(
|
||||
clientId = "irrelevant",
|
||||
scopes = listOf("irrelevant"),
|
||||
authorizationEndpoint = "irrelevant",
|
||||
tokenEndpoint = "irrelevant",
|
||||
redirectUri = "irrelevant"
|
||||
)
|
||||
|
||||
return OAuthConfigurationProvider(
|
||||
configurations = mapOf(
|
||||
listOf("imap.gmail.com", "smtp.gmail.com") to googleConfig
|
||||
),
|
||||
googleConfiguration = googleConfig
|
||||
)
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
15
app/autodiscovery/thunderbird/build.gradle.kts
Normal file
15
app/autodiscovery/thunderbird/build.gradle.kts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@Suppress("DSL_SCOPE_VIOLATION")
|
||||
plugins {
|
||||
id(ThunderbirdPlugins.Library.jvm)
|
||||
alias(libs.plugins.android.lint)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.app.autodiscovery.api)
|
||||
|
||||
compileOnly(libs.xmlpull)
|
||||
implementation(libs.okhttp)
|
||||
|
||||
testImplementation(libs.kxml2)
|
||||
testImplementation(libs.okhttp.mockwebserver)
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.fsck.k9.autodiscovery.thunderbird
|
||||
|
||||
import com.fsck.k9.logging.Timber
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
||||
class ThunderbirdAutoconfigFetcher(private val okHttpClient: OkHttpClient) {
|
||||
|
||||
fun fetchAutoconfigFile(url: HttpUrl): InputStream? {
|
||||
return try {
|
||||
val request = Request.Builder().url(url).build()
|
||||
|
||||
val response = okHttpClient.newCall(request).execute()
|
||||
|
||||
if (response.isSuccessful) {
|
||||
response.body?.byteStream()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Timber.d(e, "Error fetching URL: %s", url)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
package com.fsck.k9.autodiscovery.thunderbird
|
||||
|
||||
import com.fsck.k9.autodiscovery.api.DiscoveredServerSettings
|
||||
import com.fsck.k9.autodiscovery.api.DiscoveryResults
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import com.fsck.k9.mail.ConnectionSecurity
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.InputStreamReader
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import org.xmlpull.v1.XmlPullParserException
|
||||
import org.xmlpull.v1.XmlPullParserFactory
|
||||
|
||||
/**
|
||||
* Parser for Thunderbird's
|
||||
* [Autoconfig file format](https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat)
|
||||
*/
|
||||
class ThunderbirdAutoconfigParser {
|
||||
fun parseSettings(stream: InputStream, email: String): DiscoveryResults? {
|
||||
val factory = XmlPullParserFactory.newInstance()
|
||||
val xpp = factory.newPullParser()
|
||||
|
||||
xpp.setInput(InputStreamReader(stream))
|
||||
|
||||
val incomingServers = mutableListOf<DiscoveredServerSettings>()
|
||||
val outgoingServers = mutableListOf<DiscoveredServerSettings>()
|
||||
var eventType = xpp.eventType
|
||||
while (eventType != XmlPullParser.END_DOCUMENT) {
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
when (xpp.name) {
|
||||
"incomingServer" -> {
|
||||
incomingServers += parseServer(xpp, "incomingServer", email)
|
||||
}
|
||||
"outgoingServer" -> {
|
||||
outgoingServers += parseServer(xpp, "outgoingServer", email)
|
||||
}
|
||||
}
|
||||
}
|
||||
eventType = xpp.next()
|
||||
}
|
||||
return DiscoveryResults(incomingServers, outgoingServers)
|
||||
}
|
||||
|
||||
private fun parseServer(xpp: XmlPullParser, nodeName: String, email: String): DiscoveredServerSettings {
|
||||
val type = xpp.getAttributeValue(null, "type")
|
||||
var host: String? = null
|
||||
var username: String? = null
|
||||
var port: Int? = null
|
||||
var authType: AuthType? = null
|
||||
var connectionSecurity: ConnectionSecurity? = null
|
||||
|
||||
var eventType = xpp.eventType
|
||||
while (!(eventType == XmlPullParser.END_TAG && nodeName == xpp.name)) {
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
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()
|
||||
}
|
||||
|
||||
return DiscoveredServerSettings(type, host!!, port!!, connectionSecurity!!, authType, username)
|
||||
}
|
||||
|
||||
private fun parseAuthType(authentication: String): AuthType? {
|
||||
return when (authentication) {
|
||||
"password-cleartext" -> AuthType.PLAIN
|
||||
"TLS-client-cert" -> AuthType.EXTERNAL
|
||||
"secure" -> AuthType.CRAM_MD5
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseSocketType(socketType: String): ConnectionSecurity? {
|
||||
return when (socketType) {
|
||||
"plain" -> ConnectionSecurity.NONE
|
||||
"SSL" -> ConnectionSecurity.SSL_TLS_REQUIRED
|
||||
"STARTTLS" -> ConnectionSecurity.STARTTLS_REQUIRED
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(XmlPullParserException::class, IOException::class)
|
||||
private fun getText(xpp: XmlPullParser): String {
|
||||
val eventType = xpp.next()
|
||||
return if (eventType != XmlPullParser.TEXT) "" else xpp.text
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package com.fsck.k9.autodiscovery.thunderbird
|
||||
|
||||
import com.fsck.k9.helper.EmailHelper
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
|
||||
class ThunderbirdAutoconfigUrlProvider {
|
||||
fun getAutoconfigUrls(email: String): List<HttpUrl> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.fsck.k9.autodiscovery.thunderbird
|
||||
|
||||
import com.fsck.k9.autodiscovery.api.ConnectionSettingsDiscovery
|
||||
import com.fsck.k9.autodiscovery.api.DiscoveryResults
|
||||
|
||||
class ThunderbirdDiscovery(
|
||||
private val urlProvider: ThunderbirdAutoconfigUrlProvider,
|
||||
private val fetcher: ThunderbirdAutoconfigFetcher,
|
||||
private val parser: ThunderbirdAutoconfigParser
|
||||
) : ConnectionSettingsDiscovery {
|
||||
|
||||
override fun discover(email: String): DiscoveryResults? {
|
||||
val autoconfigUrls = urlProvider.getAutoconfigUrls(email)
|
||||
|
||||
return autoconfigUrls
|
||||
.asSequence()
|
||||
.mapNotNull { autoconfigUrl ->
|
||||
fetcher.fetchAutoconfigFile(autoconfigUrl)?.use { inputStream ->
|
||||
parser.parseSettings(inputStream, email)
|
||||
}
|
||||
}
|
||||
.firstOrNull { result ->
|
||||
result.incoming.isNotEmpty() || result.outgoing.isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = "Thunderbird autoconfig"
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package com.fsck.k9.autodiscovery.thunderbird
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isNull
|
||||
import kotlin.test.assertNotNull
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.Test
|
||||
|
||||
class ThunderbirdAutoconfigFetcherTest {
|
||||
private val fetcher = ThunderbirdAutoconfigFetcher(OkHttpClient.Builder().build())
|
||||
|
||||
@Test
|
||||
fun shouldHandleNonexistentUrl() {
|
||||
val nonExistentUrl =
|
||||
"https://autoconfig.domain.invalid/mail/config-v1.1.xml?emailaddress=test%40domain.example".toHttpUrl()
|
||||
|
||||
val inputStream = fetcher.fetchAutoconfigFile(nonExistentUrl)
|
||||
|
||||
assertThat(inputStream).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldHandleEmptyResponse() {
|
||||
val server = MockWebServer().apply {
|
||||
this.enqueue(
|
||||
MockResponse()
|
||||
.setBody("")
|
||||
.setResponseCode(204),
|
||||
)
|
||||
start()
|
||||
}
|
||||
val url = server.url("/empty/")
|
||||
|
||||
val inputStream = fetcher.fetchAutoconfigFile(url)
|
||||
|
||||
assertNotNull(inputStream) { inputStream ->
|
||||
assertThat(inputStream.available()).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
package com.fsck.k9.autodiscovery.thunderbird
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isNotNull
|
||||
import com.fsck.k9.autodiscovery.api.DiscoveredServerSettings
|
||||
import com.fsck.k9.autodiscovery.api.DiscoveryResults
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import com.fsck.k9.mail.ConnectionSecurity
|
||||
import org.junit.Test
|
||||
|
||||
class ThunderbirdAutoconfigTest {
|
||||
private val parser = ThunderbirdAutoconfigParser()
|
||||
|
||||
@Test
|
||||
fun settingsExtract() {
|
||||
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>
|
||||
|
||||
<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()
|
||||
|
||||
val connectionSettings = parser.parseSettings(input, "test@metacode.biz")
|
||||
|
||||
assertThat(connectionSettings).isNotNull()
|
||||
assertThat(connectionSettings!!.incoming).isNotNull()
|
||||
assertThat(connectionSettings.outgoing).isNotNull()
|
||||
with(connectionSettings.incoming.first()) {
|
||||
assertThat(host).isEqualTo("imap.googlemail.com")
|
||||
assertThat(port).isEqualTo(993)
|
||||
assertThat(username).isEqualTo("test@metacode.biz")
|
||||
}
|
||||
with(connectionSettings.outgoing.first()) {
|
||||
assertThat(host).isEqualTo("smtp.googlemail.com")
|
||||
assertThat(port).isEqualTo(465)
|
||||
assertThat(username).isEqualTo("test@metacode.biz")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun multipleServers() {
|
||||
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>
|
||||
|
||||
<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>
|
||||
</clientConfig>
|
||||
""".trimIndent().byteInputStream()
|
||||
|
||||
val discoveryResults = parser.parseSettings(input, "test@metacode.biz")
|
||||
|
||||
assertThat(discoveryResults).isNotNull()
|
||||
assertThat(discoveryResults!!.outgoing).isNotNull()
|
||||
with(discoveryResults.outgoing[0]) {
|
||||
assertThat(host).isEqualTo("first")
|
||||
assertThat(port).isEqualTo(465)
|
||||
assertThat(username).isEqualTo("test@metacode.biz")
|
||||
}
|
||||
with(discoveryResults.outgoing[1]) {
|
||||
assertThat(host).isEqualTo("second")
|
||||
assertThat(port).isEqualTo(465)
|
||||
assertThat(username).isEqualTo("test@metacode.biz")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun invalidResponse() {
|
||||
val input =
|
||||
"""
|
||||
<?xml version="1.0"?>
|
||||
<clientConfig version="1.1">
|
||||
<emailProvider id="metacode.biz">
|
||||
<domain>metacode.biz</domain>
|
||||
""".trimIndent().byteInputStream()
|
||||
|
||||
val connectionSettings = parser.parseSettings(input, "test@metacode.biz")
|
||||
|
||||
assertThat(connectionSettings).isEqualTo(DiscoveryResults(listOf(), listOf()))
|
||||
}
|
||||
|
||||
@Test
|
||||
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>
|
||||
""".trimIndent().byteInputStream()
|
||||
|
||||
val connectionSettings = parser.parseSettings(input, "test@metacode.biz")
|
||||
|
||||
assertThat(connectionSettings).isEqualTo(
|
||||
DiscoveryResults(
|
||||
listOf(
|
||||
DiscoveredServerSettings(
|
||||
protocol = "imap",
|
||||
host = "imap.googlemail.com",
|
||||
port = 993,
|
||||
security = ConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
authType = AuthType.PLAIN,
|
||||
username = "test@metacode.biz"
|
||||
)
|
||||
),
|
||||
listOf()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.fsck.k9.autodiscovery.thunderbird
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.containsExactly
|
||||
import org.junit.Test
|
||||
|
||||
class ThunderbirdAutoconfigUrlProviderTest {
|
||||
private val urlProvider = ThunderbirdAutoconfigUrlProvider()
|
||||
|
||||
@Test
|
||||
fun `getAutoconfigUrls with ASCII email address`() {
|
||||
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",
|
||||
"http://domain.example/.well-known/autoconfig/mail/config-v1.1.xml",
|
||||
"https://autoconfig.thunderbird.net/v1.1/domain.example"
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue