pub mod misc;
pub mod mx;
pub mod smtp;
pub mod syntax;
mod util;
use futures::future;
use misc::{check_misc, MiscDetails};
use mx::check_mx;
use smtp::{check_smtp, SmtpDetails, SmtpError};
use syntax::check_syntax;
use util::constants::LOG_TARGET;
pub use util::input_output::*;
fn calculate_reachable(misc: &MiscDetails, smtp: &Result<SmtpDetails, SmtpError>) -> Reachable {
if let Ok(smtp) = smtp {
if misc.is_disposable || misc.is_role_account || smtp.is_catch_all || smtp.has_full_inbox {
return Reachable::Risky;
}
if !smtp.is_deliverable || !smtp.can_connect_smtp || smtp.is_disabled {
return Reachable::Invalid;
}
Reachable::Safe
} else {
Reachable::Unknown
}
}
async fn check_single_email(input: CheckEmailInput) -> CheckEmailOutput {
let to_email = &input.to_emails[0];
log::debug!(
target: LOG_TARGET,
"email={} Checking email \"{}\"",
to_email,
to_email
);
let my_syntax = check_syntax(to_email.as_ref());
if !my_syntax.is_valid_syntax {
return CheckEmailOutput {
input: to_email.to_string(),
is_reachable: Reachable::Invalid,
syntax: my_syntax,
..Default::default()
};
}
log::debug!(
target: LOG_TARGET,
"email={} Found the following syntax validation: {:?}",
to_email,
my_syntax
);
let my_mx = match check_mx(&my_syntax).await {
Ok(m) => m,
e => {
return CheckEmailOutput {
input: to_email.to_string(),
is_reachable: Reachable::Unknown,
mx: e,
syntax: my_syntax,
..Default::default()
};
}
};
if my_mx.lookup.is_err() {
return CheckEmailOutput {
input: to_email.to_string(),
is_reachable: Reachable::Invalid,
mx: Ok(my_mx),
syntax: my_syntax,
..Default::default()
};
}
log::debug!(
target: LOG_TARGET,
"email={} Found the following MX hosts: {:?}",
to_email,
my_mx
.lookup
.as_ref()
.expect("If lookup is error, we already returned. qed.")
.iter()
.map(|host| host.exchange().to_string())
.collect::<Vec<String>>()
);
let my_misc = check_misc(&my_syntax);
log::debug!(
target: LOG_TARGET,
"email={} Found the following misc details: {:?}",
to_email,
my_misc
);
let mut my_smtp: Option<Result<SmtpDetails, SmtpError>> = None;
for host in my_mx
.lookup
.as_ref()
.expect("If lookup is error, we already returned. qed.")
.iter()
{
let res = check_smtp(
my_syntax
.address
.as_ref()
.expect("We already checked that the email has valid format. qed."),
host.exchange(),
input.smtp_port,
my_syntax.domain.as_ref(),
&input,
)
.await;
let is_reachable = res.is_ok();
my_smtp = Some(res);
if !is_reachable {
continue; } else {
break; }
}
let my_smtp = my_smtp.expect(
"As long as lookup has at least 1 element (which we checked), my_smtp will be a Some. qed.",
);
CheckEmailOutput {
input: to_email.to_string(),
is_reachable: calculate_reachable(&my_misc, &my_smtp),
misc: Ok(my_misc),
mx: Ok(my_mx),
smtp: my_smtp,
syntax: my_syntax,
}
}
pub async fn check_email(inputs: &CheckEmailInput) -> Vec<CheckEmailOutput> {
if inputs.to_emails.len() > 1 {
log::warn!(
target: LOG_TARGET,
"Verifying multiple emails with this library is still a beta feature, please don't overuse it. See reacherhq/check-if-email-exists #65."
);
}
let inputs = inputs.to_emails.iter().map(|email| {
CheckEmailInput {
to_emails: vec![email.clone()],
from_email: inputs.from_email.clone(),
hello_name: inputs.hello_name.clone(),
proxy: inputs.proxy.clone(),
retries: inputs.retries,
smtp_port: inputs.smtp_port,
smtp_security: inputs.smtp_security,
smtp_timeout: inputs.smtp_timeout,
yahoo_use_api: inputs.yahoo_use_api,
}
});
future::join_all(inputs.map(check_single_email)).await
}