dust-mail 0.4.0

A multi protocol email client
#[cfg(feature = "autoconfig")]
use autoconfig::{self, config};

#[cfg(feature = "autodiscover")]
use ms_autodiscover::types as autodiscover;

use crate::client::connection::ConnectionSecurity;

use super::{
    config::OAuth2Config, error::Result, AuthenticationType, Config, ConfigType, ServerConfig,
    ServerConfigType,
};

#[cfg(feature = "autoconfig")]
pub struct AutoConfigParser;

#[cfg(feature = "autoconfig")]
impl AutoConfigParser {
    fn map_server_to_server_config(server: &config::Server) -> Option<ServerConfig> {
        {
            let domain: String = server.hostname()?.into();

            let port: u16 = server.port()?.clone();

            let security: ConnectionSecurity = match server.security_type() {
                Some(security) => match security {
                    config::SecurityType::Plain => ConnectionSecurity::Plain,
                    config::SecurityType::Starttls => ConnectionSecurity::StartTls,
                    config::SecurityType::Tls => ConnectionSecurity::Tls,
                },
                None => return None,
            };

            let auth_type: Vec<AuthenticationType> = server
                .authentication_type()
                .iter()
                .map(|authentication_type| match authentication_type {
                    config::AuthenticationType::None => AuthenticationType::None,
                    config::AuthenticationType::PasswordCleartext => AuthenticationType::ClearText,
                    config::AuthenticationType::PasswordEncrypted => AuthenticationType::Encrypted,
                    config::AuthenticationType::OAuth2 => AuthenticationType::OAuth2,
                    _ => AuthenticationType::Unknown,
                })
                .collect();

            let server_type = match server.server_type() {
                config::ServerType::Imap => ServerConfigType::Imap,
                config::ServerType::Pop3 => ServerConfigType::Pop,
                config::ServerType::Smtp => ServerConfigType::Smtp,
                config::ServerType::Exchange => ServerConfigType::Exchange,
            };

            let server_config = ServerConfig::new(server_type, port, domain, security, auth_type);

            Some(server_config)
        }
    }

    pub fn parse(autoconfig: config::Config) -> Config {
        let provider: String = autoconfig.email_provider().id().into();

        let display_name: Option<String> = autoconfig
            .email_provider()
            .display_name()
            .map(|name| name.to_string());

        let incoming: Vec<ServerConfig> = autoconfig
            .email_provider()
            .incoming_servers()
            .iter()
            .filter_map(|server| Self::map_server_to_server_config(server))
            .collect();

        let outgoing: Vec<ServerConfig> = autoconfig
            .email_provider()
            .outgoing_servers()
            .iter()
            .filter_map(|server| Self::map_server_to_server_config(server))
            .collect();

        let config_type = ConfigType::MultiServer { incoming, outgoing };

        let oauth2_config = autoconfig.oauth2().map(|config| {
            OAuth2Config::new(
                config.token_url().into(),
                config.auth_url().into(),
                config.scope(),
            )
        });

        Config::new(config_type, provider, oauth2_config, display_name)
    }
}

#[cfg(feature = "autodiscover")]
pub struct AutodiscoverParser;

#[cfg(feature = "autodiscover")]
impl AutodiscoverParser {
    fn account_to_server_config(account: &autodiscover::pox::Account) -> Option<ServerConfig> {
        use autodiscover::pox::Type::*;

        let protocol = account.protocol()?;

        let port = protocol.port()?.clone();
        let domain = protocol.server()?;
        let security = if protocol.ssl() {
            ConnectionSecurity::Tls
        } else {
            ConnectionSecurity::Plain
        };

        let auth_type = vec![AuthenticationType::ClearText];

        let protocol_type = protocol.r#type()?;

        match protocol_type {
            Imap => {
                let server_config =
                    ServerConfig::new(ServerConfigType::Imap, port, domain, security, auth_type);

                return Some(server_config);
            }
            Smtp => {
                let server_config =
                    ServerConfig::new(ServerConfigType::Smtp, port, domain, security, auth_type);

                return Some(server_config);
            }
            _ => None,
        }
    }

    pub fn parse(response: autodiscover::response::AutodiscoverResponse) -> Result<Config> {
        use autodiscover::response::AutodiscoverResponse::*;

        let mut providers: Vec<String> = Vec::new();
        let mut display_name = None;

        let mut incoming = Vec::new();
        let mut outgoing = Vec::new();

        match &response {
            Pox(response) => {
                if let Some(user) = response.user() {
                    display_name = user.display_name();
                }

                for account in response.account() {
                    if let Some(protocol) = account.protocol() {
                        if let Some(domain_name) = protocol.domain_name() {
                            providers.push(domain_name.to_string());
                        }
                    }

                    if let Some(server_config) = Self::account_to_server_config(account) {
                        if server_config.r#type().is_outgoing() {
                            outgoing.push(server_config)
                        } else {
                            incoming.push(server_config)
                        }
                    }
                }
            }
        };

        let config_type = ConfigType::MultiServer { incoming, outgoing };

        let provider = match providers.first() {
            Some(provider) => provider,
            None => "Unknown",
        };

        let config = Config::new(config_type, provider, None, display_name);

        Ok(config)
    }
}

pub struct DnsDiscoverParser;

impl DnsDiscoverParser {
    pub fn parse(servers: Vec<dns_mail_discover::server::Server>) -> Config {
        use dns_mail_discover::server::ServerType::*;

        let mut incoming = Vec::new();
        let mut outgoing = Vec::new();

        for server in servers {
            let security = if server.protocol().secure() {
                ConnectionSecurity::Tls
            } else {
                ConnectionSecurity::Plain
            };

            let port = server.port();
            let domain = server.domain();
            let auth_type = vec![AuthenticationType::ClearText];

            match server.protocol().r#type() {
                Imap => {
                    let server_config = ServerConfig::new(
                        ServerConfigType::Imap,
                        port,
                        domain,
                        security,
                        auth_type,
                    );

                    incoming.push(server_config)
                }
                Pop => {
                    let server_config =
                        ServerConfig::new(ServerConfigType::Pop, port, domain, security, auth_type);

                    incoming.push(server_config)
                }
                Smtp => {
                    let server_config = ServerConfig::new(
                        ServerConfigType::Smtp,
                        port,
                        domain,
                        security,
                        auth_type,
                    );

                    outgoing.push(server_config)
                }
            }
        }

        let provider = match incoming.first() {
            Some(server) => server.domain().to_string(),
            None => match outgoing.first() {
                Some(server) => server.domain().to_string(),
                None => String::from("Unknown"),
            },
        };

        let config_type = ConfigType::MultiServer { incoming, outgoing };

        Config::new(config_type, provider, None, None::<String>)
    }
}