use client::Client;
use futures::{future::select_ok, FutureExt};
use utils::validate_email;
mod client;
pub mod config;
mod dns;
pub mod error;
mod http;
mod parse;
mod utils;
const AT_SYMBOL: char = '@';
use config::Config;
use error::{Error, ErrorKind, Result};
pub async fn from_domain<D: AsRef<str>>(domain: D) -> Result<Config> {
let mut errors: Vec<_> = Vec::new();
let client = Client::new().await?;
let mut futures = Vec::new();
let mut urls = vec![
format!("http://autoconfig.{}/mail/config-v1.1.xml", domain.as_ref()),
format!(
"http://{}/.well-known/autoconfig/mail/config-v1.1.xml",
domain.as_ref()
),
format!(
"https://autoconfig.thunderbird.net/v1.1/{}",
domain.as_ref()
),
];
match client.get_url_from_txt(domain.as_ref()).await {
Ok(txt_urls) => {
for url in txt_urls {
urls.push(url)
}
}
Err(error) => errors.push(error),
};
urls.sort();
urls.dedup();
for url in urls {
let future = client.get_config(url);
futures.push(future.boxed());
}
let result = select_ok(futures).await;
match result {
Ok((config, _remaining)) => return Ok(config),
Err(error) => errors.push(error),
}
Err(Error::new(
ErrorKind::NotFound(errors),
"Could not find a valid config",
))
}
pub async fn from_addr(email_address: &str) -> Result<Config> {
if !validate_email(email_address) {
return Err(Error::new(
ErrorKind::BadInput,
"Given email address is invalid",
));
};
let mut split = email_address.split(AT_SYMBOL);
split.next();
let domain = match split.next() {
Some(domain) => domain,
None => {
return Err(Error::new(
ErrorKind::BadInput,
"An email address must specify a domain after the '@' symbol",
))
}
};
from_domain(domain).await
}
#[cfg(test)]
mod test;