use crate::error::{SynapseError, Result};
use crate::types::SecureMessage;
use crate::synapse::blockchain::serialization::{DateTimeWrapper, UuidWrapper};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use std::time::{SystemTime, UNIX_EPOCH};
use tracing::{info, error, debug};
use uuid::Uuid;
use chrono::Utc;
pub struct SynapseSmtpServer {
config: SmtpServerConfig,
message_store: Arc<Mutex<HashMap<String, Vec<SecureMessage>>>>,
clients: Arc<Mutex<HashMap<String, ClientSession>>>,
auth_handler: Arc<dyn AuthHandler + Send + Sync>,
metrics: Arc<Mutex<ServerMetrics>>,
}
#[derive(Debug, Clone)]
pub struct SmtpServerConfig {
pub port: u16,
pub max_message_size: usize,
pub require_auth: bool,
pub tls_config: Option<TlsConfig>,
pub performance: PerformanceConfig,
}
#[derive(Debug, Clone)]
pub struct PerformanceConfig {
pub max_connections: usize,
pub keep_alive_timeout: u64,
pub enable_pipelining: bool,
pub enable_compression: bool,
}
#[derive(Debug, Clone)]
pub struct TlsConfig {
pub cert_path: String,
pub key_path: String,
pub require_tls: bool,
}
#[derive(Debug)]
struct ClientSession {
#[allow(dead_code)]
id: String,
authenticated: bool,
current_message: Option<SmtpMessage>,
#[allow(dead_code)]
connected_at: SystemTime,
}
#[derive(Debug)]
struct SmtpMessage {
#[allow(dead_code)]
from: String,
to: Vec<String>,
#[allow(dead_code)]
data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct ServerMetrics {
#[allow(dead_code)]
messages_received: u64,
#[allow(dead_code)]
messages_delivered: u64,
connections_accepted: u64,
#[allow(dead_code)]
average_processing_time_ms: f64,
#[allow(dead_code)]
last_reset: SystemTime,
}
impl Default for ServerMetrics {
fn default() -> Self {
Self {
messages_received: 0,
messages_delivered: 0,
connections_accepted: 0,
average_processing_time_ms: 0.0,
last_reset: SystemTime::now(),
}
}
}
pub trait AuthHandler {
fn authenticate(&self, username: &str, password: &str) -> Result<bool>;
fn is_authorized_sender(&self, email: &str) -> Result<bool>;
fn is_authorized_recipient(&self, email: &str) -> Result<bool>;
}
impl Default for SmtpServerConfig {
fn default() -> Self {
Self {
port: 2525, max_message_size: 25 * 1024 * 1024, require_auth: true,
tls_config: None,
performance: PerformanceConfig {
max_connections: 100,
keep_alive_timeout: 300,
enable_pipelining: true,
enable_compression: true,
},
}
}
}
impl SynapseSmtpServer {
pub fn new(
config: SmtpServerConfig,
auth_handler: Arc<dyn AuthHandler + Send + Sync>,
) -> Self {
Self {
config,
message_store: Arc::new(Mutex::new(HashMap::new())),
clients: Arc::new(Mutex::new(HashMap::new())),
auth_handler,
metrics: Arc::new(Mutex::new(ServerMetrics::default())),
}
}
pub async fn start(&self) -> Result<()> {
let addr = format!("0.0.0.0:{}", self.config.port);
let listener = TcpListener::bind(&addr).await
.map_err(|e| SynapseError::NetworkError(format!("Failed to bind SMTP server to {}: {}", addr, e)))?;
info!("EMRP SMTP Server listening on {}", addr);
loop {
match listener.accept().await {
Ok((stream, addr)) => {
info!("New SMTP connection from {}", addr);
{
let mut metrics = self.metrics.lock().unwrap();
metrics.connections_accepted += 1;
}
let server = self.clone();
tokio::spawn(async move {
if let Err(e) = server.handle_connection(stream).await {
error!("SMTP connection error: {}", e);
}
});
}
Err(e) => {
error!("Failed to accept SMTP connection: {}", e);
}
}
}
}
async fn handle_connection(&self, stream: TcpStream) -> Result<()> {
let (read_half, write_half) = stream.into_split();
let mut reader = BufReader::new(read_half);
let mut writer = write_half;
let mut session = ClientSession {
id: format!("smtp_{}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis()),
authenticated: false,
current_message: None,
connected_at: SystemTime::now(),
};
writer.write_all(b"220 EMRP SMTP Server Ready\r\n").await?;
writer.flush().await?;
let mut line = String::new();
while reader.read_line(&mut line).await? > 0 {
let command = line.trim();
debug!("SMTP command: {}", command);
let response = self.process_smtp_command(command, &mut session).await?;
writer.write_all(response.as_bytes()).await?;
writer.flush().await?;
if response.starts_with("221") {
break;
}
line.clear();
}
Ok(())
}
async fn process_smtp_command(&self, command: &str, session: &mut ClientSession) -> Result<String> {
let parts: Vec<&str> = command.split_whitespace().collect();
if parts.is_empty() {
return Ok("500 Syntax error\r\n".to_string());
}
let cmd = parts[0].to_uppercase();
match cmd.as_str() {
"HELO" | "EHLO" => {
let hostname = parts.get(1).unwrap_or(&"unknown");
if cmd == "EHLO" {
let mut response = format!("250-Hello {}\r\n", hostname);
response.push_str("250-PIPELINING\r\n");
response.push_str("250-8BITMIME\r\n");
if self.config.require_auth {
response.push_str("250-AUTH PLAIN LOGIN\r\n");
}
response.push_str("250 Ok\r\n");
Ok(response)
} else {
Ok(format!("250 Hello {}\r\n", hostname))
}
}
"AUTH" => {
if parts.len() < 2 {
return Ok("500 AUTH mechanism required\r\n".to_string());
}
let mechanism = parts[1].to_uppercase();
match mechanism.as_str() {
"PLAIN" => {
if parts.len() >= 3 {
if let Ok(auth_data) = base64_decode(parts[2]) {
if let Ok(auth_str) = String::from_utf8(auth_data) {
let auth_parts: Vec<&str> = auth_str.split('\0').collect();
if auth_parts.len() >= 3 {
let username = auth_parts[1];
let password = auth_parts[2];
match self.auth_handler.authenticate(username, password) {
Ok(true) => {
session.authenticated = true;
return Ok("235 Authentication successful\r\n".to_string());
}
Ok(false) => {
return Ok("535 Authentication failed\r\n".to_string());
}
Err(_) => {
return Ok("454 Temporary authentication failure\r\n".to_string());
}
}
}
}
}
}
Ok("334 \r\n".to_string()) }
_ => Ok("504 Unrecognized authentication type\r\n".to_string())
}
}
"MAIL" => {
if self.config.require_auth && !session.authenticated {
return Ok("530 Authentication required\r\n".to_string());
}
if let Some(from_addr) = self.extract_email_from_mail_from(command) {
match self.auth_handler.is_authorized_sender(&from_addr) {
Ok(true) => {
session.current_message = Some(SmtpMessage {
from: from_addr,
to: Vec::new(),
data: Vec::new(),
});
Ok("250 Ok\r\n".to_string())
}
Ok(false) => Ok("550 Sender not authorized\r\n".to_string()),
Err(_) => Ok("451 Temporary failure\r\n".to_string()),
}
} else {
Ok("501 Syntax error in MAIL command\r\n".to_string())
}
}
"RCPT" => {
if session.current_message.is_none() {
return Ok("503 Need MAIL command first\r\n".to_string());
}
if let Some(to_addr) = self.extract_email_from_rcpt_to(command) {
match self.auth_handler.is_authorized_recipient(&to_addr) {
Ok(true) => {
if let Some(ref mut msg) = session.current_message {
msg.to.push(to_addr);
}
Ok("250 Ok\r\n".to_string())
}
Ok(false) => Ok("550 Recipient not authorized\r\n".to_string()),
Err(_) => Ok("451 Temporary failure\r\n".to_string()),
}
} else {
Ok("501 Syntax error in RCPT command\r\n".to_string())
}
}
"DATA" => {
if session.current_message.is_none() {
return Ok("503 Need RCPT command first\r\n".to_string());
}
if let Some(ref msg) = session.current_message {
if msg.to.is_empty() {
return Ok("503 Need RCPT command first\r\n".to_string());
}
}
Ok("354 Start mail input; end with <CRLF>.<CRLF>\r\n".to_string())
}
"QUIT" => {
Ok("221 Bye\r\n".to_string())
}
_ => {
Ok("502 Command not implemented\r\n".to_string())
}
}
}
fn extract_email_from_mail_from(&self, command: &str) -> Option<String> {
if let Some(start) = command.find('<') {
if let Some(end) = command.find('>') {
if end > start {
return Some(command[start + 1..end].to_string());
}
}
}
None
}
fn extract_email_from_rcpt_to(&self, command: &str) -> Option<String> {
if let Some(start) = command.find('<') {
if let Some(end) = command.find('>') {
if end > start {
return Some(command[start + 1..end].to_string());
}
}
}
None
}
#[allow(dead_code)]
async fn store_message(&self, message: SmtpMessage) -> Result<()> {
let start_time = SystemTime::now();
let secure_message = SecureMessage {
message_id: UuidWrapper::new(Uuid::new_v4()),
to_global_id: message.to[0].clone(), from_global_id: message.from,
encrypted_content: message.data,
signature: Vec::new(),
timestamp: DateTimeWrapper::new(Utc::now()),
security_level: crate::types::SecurityLevel::Private,
routing_path: Vec::new(),
metadata: std::collections::HashMap::new(),
};
{
let mut store = self.message_store.lock().unwrap();
for recipient in &message.to {
let messages = store.entry(recipient.clone()).or_insert_with(Vec::new);
messages.push(secure_message.clone());
}
}
{
let mut metrics = self.metrics.lock().unwrap();
metrics.messages_received += 1;
if let Ok(elapsed) = start_time.elapsed() {
let processing_time = elapsed.as_millis() as f64;
metrics.average_processing_time_ms =
(metrics.average_processing_time_ms + processing_time) / 2.0;
}
}
info!("Stored message from {} to {:?}", secure_message.from_global_id, message.to);
Ok(())
}
pub fn get_messages(&self, recipient: &str) -> Result<Vec<SecureMessage>> {
let store = self.message_store.lock().unwrap();
Ok(store.get(recipient).cloned().unwrap_or_default())
}
pub fn get_metrics(&self) -> ServerMetrics {
self.metrics.lock().unwrap().clone()
}
}
impl Clone for SynapseSmtpServer {
fn clone(&self) -> Self {
Self {
config: self.config.clone(),
message_store: Arc::clone(&self.message_store),
clients: Arc::clone(&self.clients),
auth_handler: Arc::clone(&self.auth_handler),
metrics: Arc::clone(&self.metrics),
}
}
}
fn base64_decode(input: &str) -> Result<Vec<u8>> {
const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let input = input.trim();
let mut result = Vec::new();
let mut buffer = 0u32;
let mut bits = 0;
for byte in input.bytes() {
if byte == b'=' {
break;
}
let value = CHARS.iter().position(|&x| x == byte)
.ok_or_else(|| SynapseError::Crypto("Invalid base64 character".to_string()))?;
buffer = (buffer << 6) | (value as u32);
bits += 6;
if bits >= 8 {
result.push((buffer >> (bits - 8)) as u8);
bits -= 8;
}
}
Ok(result)
}