use std::io::{BufRead, BufReader, Write}; use crate::state::{Message, State}; use std::net::{TcpListener, TcpStream}; pub fn start_server(state: State) { let listener = TcpListener::bind("localhost:8025").unwrap(); for stream in listener.incoming() { match stream { Ok(tcp_stream) => { println!("Incoming connection"); let reader = BufReader::new(tcp_stream.try_clone().unwrap()); let mut connection = Connection::new(); if let Err(e) = connection.handle(tcp_stream, reader) { eprintln!("System error: {}", e) } else if let Ok(mut state_inner) = state.lock() { for mail in connection.messages { println!( "Mail from {} for {}\n{}", mail.sender, mail.recipients.join(", "), mail.data ); for r in &mail.recipients { if let Some(mailbox) = state_inner.get_mut(&r.to_lowercase()) { mailbox.messages.push(mail.clone()) } } } } } Err(e) => { eprintln!("Failed to accept connection: {}", e) } } } } const CMD_MAIL_FROM: &str = "MAIL FROM:"; const CMD_HELLO: &str = "HELO"; const CMD_RCPT_TO: &str = "RCPT TO:"; const CMD_DATA: &str = "DATA"; const CMD_DOT: &str = "."; const CMD_QUIT: &str = "QUIT"; const MSG_BANNER: &str = "220 very good mailserver"; const MSG_OK: &str = "250 ok"; const MSG_DATA: &str = "354 End data with ."; const MSG_UNEXPECTED: &str = "500 unexpected line"; const MSG_FAIL: &str = "500 mailserver is broken"; const MSG_BYE: &str = "221 Bye"; const ERR_WRITE_FAILED: &str = "Failed to write"; const ERR_READ_FAILED: &str = "Failed to read"; // => 5 chars const RCPT_MIN_LEN: usize = CMD_RCPT_TO.len() + 5; const MAIL_MIN_LEN: usize = CMD_MAIL_FROM.len() + 5; enum ConnectionState { PreHello, Hello, Mail, PreData, Data, Done, } struct IncomingMessage { sender: String, recipients: Vec, data: Option, } struct Connection { state: ConnectionState, messages: Vec, current_message: Option, } impl IncomingMessage { fn new(sender: String) -> Self { IncomingMessage { sender, recipients: Vec::new(), data: None, } } fn finalize(self) -> Message { Message { sender: self.sender, recipients: self.recipients, data: self.data.unwrap_or("".to_string()), } } } impl Connection { fn new() -> Self { Connection { state: ConnectionState::PreHello, messages: Vec::new(), current_message: None, } } fn handle( &mut self, mut writer: TcpStream, mut reader: BufReader, ) -> Result<(), &str> { send(&mut writer, MSG_BANNER)?; let mut line = String::new(); loop { let read_result = reader.read_line(&mut line); if read_result.is_err() || line.is_empty() { return Err(ERR_READ_FAILED); } println!("Read line: {}", line); let response = self.handle_line(&line); response?; if let Ok(Some(out)) = response { send(&mut writer, out)?; if out == MSG_BYE { break; } } line.clear() } Ok(()) } fn handle_line(&mut self, line: &str) -> Result, &'static str> { match self.state { ConnectionState::PreHello => { if line.starts_with(CMD_HELLO) { self.state = ConnectionState::Hello; return Ok(Some(MSG_OK)); } } ConnectionState::Hello => { if line.starts_with(CMD_MAIL_FROM) && line.len() >= MAIL_MIN_LEN { self.current_message = Some(IncomingMessage::new( line[CMD_MAIL_FROM.len() + 1..line.len() - 3].to_owned(), )); self.state = ConnectionState::Mail; return Ok(Some(MSG_OK)); } } ConnectionState::Mail => { if line.starts_with(CMD_RCPT_TO) && line.len() >= RCPT_MIN_LEN { self.add_recipient(line)?; return Ok(Some(MSG_OK)); } } ConnectionState::PreData => { if line.starts_with(CMD_RCPT_TO) && line.len() >= RCPT_MIN_LEN { self.add_recipient(line)?; return Ok(Some(MSG_OK)); } if line.starts_with(CMD_DATA) { self.state = ConnectionState::Data; return Ok(Some(MSG_DATA)); } } ConnectionState::Data => { if line.trim_end() == CMD_DOT { self.state = ConnectionState::Done; return Ok(Some(MSG_OK)); } return match self.current_message { Some(ref mut _message) => { if let Some(data) = _message.data.as_mut() { data.push_str(line); } else { _message.data = Some(String::from(line)) } Ok(None) } _ => Err(MSG_FAIL), }; } ConnectionState::Done => { if line.starts_with(CMD_QUIT) { self.finalize_mail(); return Ok(Some(MSG_BYE)); } if line.starts_with(CMD_MAIL_FROM) { self.finalize_mail(); return Ok(Some(MSG_OK)); } } } Ok(Some(MSG_UNEXPECTED)) } fn finalize_mail(&mut self) { let current_message = self.current_message.take(); if let Some(mail) = current_message { self.messages.push(mail.finalize()) } } fn add_recipient(&mut self, line: &str) -> Result<(), &'static str> { match self.current_message { Some(ref mut message) => { message .recipients .push(line[CMD_RCPT_TO.len() + 1..line.len() - 3].to_string()); self.state = ConnectionState::PreData } _ => { return Err(MSG_FAIL); } } Ok(()) } } fn send(writer: &mut TcpStream, message: &str) -> Result<(), &'static str> { if writeln!(writer, "{}", message).is_err() { return Err(ERR_WRITE_FAILED); } Ok(()) }