Files
bichon/src/modules/smtp/mod.rs
T

110 lines
3.2 KiB
Rust

//
// Copyright (c) 2025-2026 rustmailer.com (https://rustmailer.com)
//
// This file is part of the Bichon Email Archiving Project
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use std::net::SocketAddr;
use tokio::net::TcpListener;
use tokio_rustls::TlsAcceptor;
use crate::modules::{
common::signal::SIGNAL_MANAGER,
settings::cli::{SmtpEncryptionMode, SETTINGS},
smtp::{
server::{run_smtp_server, run_smtps_server},
tls::create_acceptor,
},
};
pub mod server;
pub mod stream;
pub mod tls;
#[cfg(test)]
mod tests;
#[derive(Clone, Default)]
pub struct SmtpConfig {
pub whitelist: Option<Vec<String>>,
pub tls_acceptor: Option<TlsAcceptor>,
pub auth_required: bool,
}
pub struct SmtpServer {
pub smtp_addr: SocketAddr,
smtp_handle: tokio::task::JoinHandle<()>,
}
impl SmtpServer {
pub async fn stop(self) {
let _ = self.smtp_handle.await;
}
}
pub async fn start_smtp_server() -> std::io::Result<SmtpServer> {
let smtp_port = SETTINGS.bichon_smtp_port;
let tls_acceptor: Option<TlsAcceptor> = match SETTINGS.bichon_smtp_encryption {
SmtpEncryptionMode::None => None,
SmtpEncryptionMode::Starttls | SmtpEncryptionMode::Tls => Some(create_acceptor().await?),
};
let smtp_listener = TcpListener::bind((
SETTINGS.bichon_bind_ip.clone().unwrap_or("0.0.0.0".into()),
smtp_port,
))
.await
.map_err(|e| {
if e.kind() == std::io::ErrorKind::AddrInUse {
std::io::Error::other(format!(
"SMTP port {smtp_port} is already in use. Is another instance running?"
))
} else {
e
}
})?;
let smtp_addr = smtp_listener.local_addr()?;
let smtp_config = SmtpConfig {
whitelist: None,
tls_acceptor: match SETTINGS.bichon_smtp_encryption {
SmtpEncryptionMode::None | SmtpEncryptionMode::Tls => None,
SmtpEncryptionMode::Starttls => tls_acceptor.clone(),
},
auth_required: SETTINGS.bichon_smtp_auth_required,
};
let smtp_shutdown = SIGNAL_MANAGER.subscribe();
let smtp_handle = if matches!(SETTINGS.bichon_smtp_encryption, SmtpEncryptionMode::Tls) {
let acceptor = tls_acceptor
.clone()
.expect("TLS acceptor required when tls=true");
tokio::spawn(async move {
run_smtps_server(smtp_listener, smtp_config, acceptor, smtp_shutdown).await;
})
} else {
tokio::spawn(async move {
run_smtp_server(smtp_listener, smtp_config, smtp_shutdown).await;
})
};
Ok(SmtpServer {
smtp_addr,
smtp_handle,
})
}