use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ClientAuthMode {
#[default]
Disabled,
Optional,
Required,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ProtocolKind {
Smtp,
Imap,
Pop3,
Jmap,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TlsEndpointConfig {
#[serde(alias = "cert_path")]
pub cert_path: PathBuf,
#[serde(alias = "key_path")]
pub key_path: PathBuf,
#[serde(default)]
pub client_auth: ClientAuthMode,
#[serde(default)]
pub client_ca_path: Option<PathBuf>,
}
impl TlsEndpointConfig {
pub fn validate(&self) -> anyhow::Result<()> {
if self.cert_path.as_os_str().is_empty() {
anyhow::bail!("TLS cert_path cannot be empty");
}
if self.key_path.as_os_str().is_empty() {
anyhow::bail!("TLS key_path cannot be empty");
}
if self.client_auth != ClientAuthMode::Disabled && self.client_ca_path.is_none() {
anyhow::bail!(
"TLS client_ca_path must be set when client_auth is '{}' (not 'disabled')",
match self.client_auth {
ClientAuthMode::Optional => "optional",
ClientAuthMode::Required => "required",
ClientAuthMode::Disabled => unreachable!(),
}
);
}
Ok(())
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TlsConfig {
pub default: TlsEndpointConfig,
#[serde(default)]
pub smtp: Option<TlsEndpointConfig>,
#[serde(default)]
pub imap: Option<TlsEndpointConfig>,
#[serde(default)]
pub pop3: Option<TlsEndpointConfig>,
#[serde(default)]
pub jmap: Option<TlsEndpointConfig>,
}
impl TlsConfig {
pub fn tls_for_protocol(&self, proto: ProtocolKind) -> &TlsEndpointConfig {
let override_cfg = match proto {
ProtocolKind::Smtp => self.smtp.as_ref(),
ProtocolKind::Imap => self.imap.as_ref(),
ProtocolKind::Pop3 => self.pop3.as_ref(),
ProtocolKind::Jmap => self.jmap.as_ref(),
};
override_cfg.unwrap_or(&self.default)
}
pub fn validate(&self) -> anyhow::Result<()> {
self.default
.validate()
.map_err(|e| anyhow::anyhow!("tls.default: {}", e))?;
if let Some(ref s) = self.smtp {
s.validate()
.map_err(|e| anyhow::anyhow!("tls.smtp: {}", e))?;
}
if let Some(ref i) = self.imap {
i.validate()
.map_err(|e| anyhow::anyhow!("tls.imap: {}", e))?;
}
if let Some(ref p) = self.pop3 {
p.validate()
.map_err(|e| anyhow::anyhow!("tls.pop3: {}", e))?;
}
if let Some(ref j) = self.jmap {
j.validate()
.map_err(|e| anyhow::anyhow!("tls.jmap: {}", e))?;
}
Ok(())
}
}