pub mod smtp_server;
pub mod imap_server;
pub mod connectivity;
pub mod auth;
pub use smtp_server::{SynapseSmtpServer, SmtpServerConfig, AuthHandler};
pub use imap_server::{SynapseImapServer, ImapServerConfig};
pub use connectivity::{ConnectivityDetector, ConnectivityAssessment, ServerRecommendation};
pub use auth::{SynapseAuthHandler, UserAccount, UserPermissions, create_test_auth_handler};
use crate::error::Result;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use tracing::{info, warn};
pub struct SynapseEmailServer {
smtp_server: SynapseSmtpServer,
imap_server: SynapseImapServer,
connectivity: ConnectivityAssessment,
auth_handler: Arc<SynapseAuthHandler>,
}
impl SynapseEmailServer {
pub async fn new() -> Result<Self> {
let detector = ConnectivityDetector::default();
let connectivity = detector.assess_connectivity().await?;
info!("Email server connectivity assessment: {:?}", connectivity.recommended_config);
let auth_handler = Arc::new(SynapseAuthHandler::new());
let smtp_config = match &connectivity.recommended_config {
ServerRecommendation::RunLocalServer { smtp_port, .. } => {
SmtpServerConfig {
port: *smtp_port,
..Default::default()
}
}
_ => SmtpServerConfig::default(),
};
let imap_config = match &connectivity.recommended_config {
ServerRecommendation::RunLocalServer { imap_port, .. } => {
ImapServerConfig {
port: *imap_port,
..Default::default()
}
}
_ => ImapServerConfig::default(),
};
let message_store = Arc::new(Mutex::new(HashMap::new()));
let smtp_server = SynapseSmtpServer::new(smtp_config, Arc::clone(&auth_handler) as Arc<dyn AuthHandler + Send + Sync>);
let imap_server = SynapseImapServer::new(imap_config, Arc::clone(&message_store), Arc::clone(&auth_handler) as Arc<dyn AuthHandler + Send + Sync>);
Ok(Self {
smtp_server,
imap_server,
connectivity,
auth_handler,
})
}
pub fn with_config(
smtp_config: SmtpServerConfig,
imap_config: ImapServerConfig,
connectivity: ConnectivityAssessment,
) -> Result<Self> {
let auth_handler = Arc::new(SynapseAuthHandler::new());
let message_store = Arc::new(Mutex::new(HashMap::new()));
let smtp_server = SynapseSmtpServer::new(smtp_config, Arc::clone(&auth_handler) as Arc<dyn AuthHandler + Send + Sync>);
let imap_server = SynapseImapServer::new(imap_config, Arc::clone(&message_store), Arc::clone(&auth_handler) as Arc<dyn AuthHandler + Send + Sync>);
Ok(Self {
smtp_server,
imap_server,
connectivity,
auth_handler,
})
}
pub async fn start(&self) -> Result<()> {
match &self.connectivity.recommended_config {
ServerRecommendation::RunLocalServer { smtp_port, imap_port, external_ip } => {
info!("Starting local email server on {}:{}/{}", external_ip, smtp_port, imap_port);
let smtp_server = self.smtp_server.clone();
tokio::spawn(async move {
if let Err(e) = smtp_server.start().await {
warn!("SMTP server error: {}", e);
}
});
let imap_server = self.imap_server.clone();
tokio::spawn(async move {
if let Err(e) = imap_server.start().await {
warn!("IMAP server error: {}", e);
}
});
info!("Email servers started successfully");
Ok(())
}
ServerRecommendation::RelayOnly { reason } => {
warn!("Email server in relay-only mode: {}", reason);
let smtp_server = self.smtp_server.clone();
tokio::spawn(async move {
if let Err(e) = smtp_server.start().await {
warn!("SMTP relay server error: {}", e);
}
});
Ok(())
}
ServerRecommendation::ExternalProvider { reason } => {
warn!("Using external email provider: {}", reason);
Ok(())
}
}
}
pub fn get_connectivity(&self) -> &ConnectivityAssessment {
&self.connectivity
}
pub fn get_auth_handler(&self) -> Arc<SynapseAuthHandler> {
Arc::clone(&self.auth_handler)
}
pub fn add_user(&self, user: UserAccount) -> Result<()> {
self.auth_handler.add_user(user)
}
pub fn add_local_domain(&self, domain: &str) -> Result<()> {
self.auth_handler.add_local_domain(domain)
}
pub fn add_relay_domain(&self, domain: &str) -> Result<()> {
self.auth_handler.add_relay_domain(domain)
}
pub fn should_use_local_server(&self) -> bool {
matches!(
self.connectivity.recommended_config,
ServerRecommendation::RunLocalServer { .. }
)
}
pub fn can_relay_for_clients(&self) -> bool {
matches!(
self.connectivity.recommended_config,
ServerRecommendation::RunLocalServer { .. } | ServerRecommendation::RelayOnly { .. }
)
}
}
pub async fn create_test_email_server() -> Result<SynapseEmailServer> {
let auth_handler = Arc::new(create_test_auth_handler());
let smtp_config = SmtpServerConfig {
port: 2525,
require_auth: false, ..Default::default()
};
let imap_config = ImapServerConfig {
port: 1143,
..Default::default()
};
let connectivity = ConnectivityAssessment {
can_bind_smtp: true,
can_bind_imap: true,
has_external_ip: false,
external_ip: None,
firewall_status: connectivity::FirewallStatus::Unknown,
recommended_config: ServerRecommendation::RunLocalServer {
smtp_port: 2525,
imap_port: 1143,
external_ip: std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)),
},
};
let message_store = Arc::new(Mutex::new(HashMap::new()));
let smtp_server = SynapseSmtpServer::new(smtp_config, Arc::clone(&auth_handler) as Arc<dyn AuthHandler + Send + Sync>);
let imap_server = SynapseImapServer::new(imap_config, Arc::clone(&message_store), Arc::clone(&auth_handler) as Arc<dyn AuthHandler + Send + Sync>);
Ok(SynapseEmailServer {
smtp_server,
imap_server,
connectivity,
auth_handler,
})
}