Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 64d8f1f7 authored by Mohammed Althaf T's avatar Mohammed Althaf T 😊 Committed by Nishith Khanna
Browse files

Add back `ProvidersXmlDiscovery`

This reverts commit be44dc3a.
parent a1c23c6c
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -4,6 +4,7 @@ import app.k9mail.feature.account.oauth.featureAccountOAuthModule
import app.k9mail.feature.launcher.di.featureLauncherModule
import app.k9mail.feature.launcher.di.featureLauncherModule
import app.k9mail.legacy.ui.folder.uiFolderModule
import app.k9mail.legacy.ui.folder.uiFolderModule
import com.fsck.k9.account.accountModule
import com.fsck.k9.account.accountModule
import com.fsck.k9.activity.accountmanager.providersxml.autodiscoveryProvidersXmlModule
import com.fsck.k9.activity.activityModule
import com.fsck.k9.activity.activityModule
import com.fsck.k9.contacts.contactsModule
import com.fsck.k9.contacts.contactsModule
import com.fsck.k9.ui.account.accountUiModule
import com.fsck.k9.ui.account.accountUiModule
@@ -42,6 +43,7 @@ val uiModules = listOf(
    changelogUiModule,
    changelogUiModule,
    messageSourceModule,
    messageSourceModule,
    accountUiModule,
    accountUiModule,
    autodiscoveryProvidersXmlModule,
    messageDetailsUiModule,
    messageDetailsUiModule,
    messageViewUiModule,
    messageViewUiModule,
    identityUiModule,
    identityUiModule,
+19 −0
Original line number Original line Diff line number Diff line
package com.fsck.k9.activity.accountmanager.providersxml

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?,
)
+8 −0
Original line number Original line Diff line number Diff line
package com.fsck.k9.activity.accountmanager.providersxml

import org.koin.dsl.module

val autodiscoveryProvidersXmlModule = module {
    factory { ProvidersXmlProvider(context = get()) }
    factory { ProvidersXmlDiscovery(xmlProvider = get(), oAuthConfigurationProvider = get()) }
}
+154 −0
Original line number Original line Diff line number Diff line
package com.fsck.k9.activity.accountmanager.providersxml

import android.content.res.XmlResourceParser
import android.net.Uri
import app.k9mail.core.common.mail.Protocols
import app.k9mail.core.common.oauth.OAuthConfigurationProvider
import com.fsck.k9.helper.EmailHelper
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ConnectionSecurity
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,
    )
}
+11 −0
Original line number Original line Diff line number Diff line
package com.fsck.k9.activity.accountmanager.providersxml

import android.content.Context
import android.content.res.XmlResourceParser
import com.fsck.k9.ui.R

class ProvidersXmlProvider(private val context: Context) {
    fun getXml(): XmlResourceParser {
        return context.resources.getXml(R.xml.providers)
    }
}
Loading