fix in smtp and split rs files
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
use lettre::message::Mailbox;
|
||||
|
||||
use crate::state::{SmtpConfig, SmtpSecurity};
|
||||
|
||||
use super::values::ConfigValues;
|
||||
|
||||
pub(super) fn load_smtp(
|
||||
values: &ConfigValues,
|
||||
) -> Result<Option<SmtpConfig>, Box<dyn std::error::Error>> {
|
||||
let Some(host) = values.optional("SMTP_HOST") else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let port = values.get("SMTP_PORT", "587").parse()?;
|
||||
let security = match values.optional("SMTP_SECURITY") {
|
||||
Some(value) => parse_smtp_security(&value)?,
|
||||
None => smtp_security_for_port(port),
|
||||
};
|
||||
|
||||
Ok(Some(SmtpConfig {
|
||||
host,
|
||||
port,
|
||||
security,
|
||||
username: values.get("SMTP_USERNAME", ""),
|
||||
password: values.get("SMTP_PASSWORD", ""),
|
||||
from: normalize_smtp_from(values.required("SMTP_FROM", "SMTP_HOST is set")?)?,
|
||||
public_url: values.required("PUBLIC_URL", "SMTP_HOST is set")?,
|
||||
}))
|
||||
}
|
||||
|
||||
fn smtp_security_for_port(port: u16) -> SmtpSecurity {
|
||||
match port {
|
||||
465 => SmtpSecurity::Tls,
|
||||
587 => SmtpSecurity::StartTls,
|
||||
_ => SmtpSecurity::None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_smtp_security(value: &str) -> Result<SmtpSecurity, Box<dyn std::error::Error>> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"none" | "plain" => Ok(SmtpSecurity::None),
|
||||
"starttls" => Ok(SmtpSecurity::StartTls),
|
||||
"tls" | "ssl" | "smtps" => Ok(SmtpSecurity::Tls),
|
||||
_ => Err("SMTP_SECURITY must be one of: none, starttls, tls".into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_smtp_from(value: String) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let trimmed = value.trim();
|
||||
if trimmed.is_empty() {
|
||||
return Err("SMTP_FROM cannot be empty".into());
|
||||
}
|
||||
if trimmed.parse::<Mailbox>().is_ok() {
|
||||
return Ok(trimmed.to_owned());
|
||||
}
|
||||
|
||||
let unquoted = match (trimmed.as_bytes().first(), trimmed.as_bytes().last()) {
|
||||
(Some(b'"'), Some(b'"')) | (Some(b'\''), Some(b'\'')) if trimmed.len() >= 2 => {
|
||||
trimmed[1..trimmed.len() - 1].trim()
|
||||
}
|
||||
_ => return Err(format!("SMTP_FROM is not a valid mailbox: {trimmed}").into()),
|
||||
};
|
||||
if unquoted.is_empty() {
|
||||
return Err("SMTP_FROM cannot be empty".into());
|
||||
}
|
||||
unquoted
|
||||
.parse::<Mailbox>()
|
||||
.map_err(|error| format!("SMTP_FROM is not a valid mailbox: {error}"))?;
|
||||
Ok(unquoted.to_owned())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn detects_security_from_standard_ports() {
|
||||
assert_eq!(smtp_security_for_port(25), SmtpSecurity::None);
|
||||
assert_eq!(smtp_security_for_port(465), SmtpSecurity::Tls);
|
||||
assert_eq!(smtp_security_for_port(587), SmtpSecurity::StartTls);
|
||||
assert_eq!(smtp_security_for_port(2525), SmtpSecurity::None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accepts_supported_security_modes() {
|
||||
assert_eq!(parse_smtp_security("none").unwrap(), SmtpSecurity::None);
|
||||
assert_eq!(parse_smtp_security("starttls").unwrap(), SmtpSecurity::StartTls);
|
||||
assert_eq!(parse_smtp_security("tls").unwrap(), SmtpSecurity::Tls);
|
||||
assert!(parse_smtp_security("auto").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalizes_smtp_from() {
|
||||
assert_eq!(
|
||||
normalize_smtp_from(" \"RustPad <rustpad@notes.example>\" ".to_owned()).unwrap(),
|
||||
"RustPad <rustpad@notes.example>"
|
||||
);
|
||||
assert!(normalize_smtp_from("RustPad".to_owned()).is_err());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user