2023-07-02 19:59:25 +02:00
|
|
|
use std::io::{BufRead, BufReader, Write};
|
2023-07-02 20:18:43 +02:00
|
|
|
|
2023-07-02 19:59:25 +02:00
|
|
|
use std::net::{TcpListener, TcpStream};
|
|
|
|
|
|
|
|
pub fn start_server() {
|
|
|
|
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();
|
2023-07-02 20:15:10 +02:00
|
|
|
if let Err(e) = connection.handle(tcp_stream, reader) {
|
|
|
|
eprintln!("System error: {}", e)
|
|
|
|
} else {
|
|
|
|
for mail in connection.messages {
|
2023-07-02 20:18:43 +02:00
|
|
|
println!(
|
|
|
|
"Mail from {} for {}\n{}",
|
|
|
|
mail.sender,
|
|
|
|
mail.recipients.join(", "),
|
|
|
|
mail.data.unwrap_or("<empty>".to_string())
|
|
|
|
)
|
2023-07-02 20:15:10 +02:00
|
|
|
}
|
|
|
|
}
|
2023-07-02 19:59:25 +02:00
|
|
|
}
|
|
|
|
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 <CR><LF>.<CR><LF>";
|
|
|
|
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";
|
|
|
|
|
|
|
|
enum ConnectionState {
|
|
|
|
PreHello,
|
|
|
|
Hello,
|
|
|
|
Mail,
|
|
|
|
PreData,
|
|
|
|
Data,
|
|
|
|
Done,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Message {
|
|
|
|
sender: String,
|
|
|
|
recipients: Vec<String>,
|
|
|
|
data: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Connection {
|
|
|
|
state: ConnectionState,
|
|
|
|
messages: Vec<Message>,
|
|
|
|
current_message: Option<Message>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Message {
|
|
|
|
fn new(sender: String) -> Self {
|
|
|
|
Message {
|
|
|
|
sender,
|
|
|
|
recipients: Vec::new(),
|
|
|
|
data: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Connection {
|
|
|
|
fn new() -> Self {
|
|
|
|
Connection {
|
|
|
|
state: ConnectionState::PreHello,
|
|
|
|
messages: Vec::new(),
|
|
|
|
current_message: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-02 20:18:43 +02:00
|
|
|
fn handle(
|
|
|
|
&mut self,
|
|
|
|
mut writer: TcpStream,
|
|
|
|
mut reader: BufReader<TcpStream>,
|
|
|
|
) -> Result<(), &str> {
|
2023-07-02 19:59:25 +02:00
|
|
|
send(&mut writer, MSG_BANNER)?;
|
|
|
|
|
|
|
|
let mut line = String::new();
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let read_result = reader.read_line(&mut line);
|
|
|
|
|
2023-07-02 20:18:43 +02:00
|
|
|
if read_result.is_err() || line.is_empty() {
|
2023-07-02 19:59:25 +02:00
|
|
|
return Err(ERR_READ_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
println!("Read line: {}", line);
|
|
|
|
|
|
|
|
let response = self.handle_line(&line);
|
|
|
|
|
2023-07-02 20:18:43 +02:00
|
|
|
response?;
|
2023-07-02 19:59:25 +02:00
|
|
|
|
|
|
|
if let Ok(Some(out)) = response {
|
|
|
|
send(&mut writer, out)?;
|
|
|
|
|
|
|
|
if out == MSG_BYE {
|
2023-07-02 20:15:10 +02:00
|
|
|
break;
|
2023-07-02 19:59:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
line.clear()
|
|
|
|
}
|
|
|
|
|
2023-07-02 20:18:43 +02:00
|
|
|
Ok(())
|
2023-07-02 19:59:25 +02:00
|
|
|
}
|
|
|
|
|
2023-07-02 20:18:43 +02:00
|
|
|
fn handle_line(&mut self, line: &str) -> Result<Option<&str>, &'static str> {
|
2023-07-02 19:59:25 +02:00
|
|
|
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) {
|
2023-07-02 20:18:43 +02:00
|
|
|
self.current_message = Some(Message::new(line.to_owned()));
|
2023-07-02 19:59:25 +02:00
|
|
|
self.state = ConnectionState::Mail;
|
|
|
|
return Ok(Some(MSG_OK));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ConnectionState::Mail => {
|
|
|
|
if line.starts_with(CMD_RCPT_TO) {
|
|
|
|
self.add_recipient(line)?;
|
|
|
|
return Ok(Some(MSG_OK));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ConnectionState::PreData => {
|
|
|
|
if line.starts_with(CMD_RCPT_TO) {
|
|
|
|
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;
|
2023-07-02 20:15:10 +02:00
|
|
|
return Ok(Some(MSG_OK));
|
2023-07-02 19:59:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return match self.current_message {
|
|
|
|
Some(ref mut _message) => {
|
|
|
|
if let Some(ref mut d) = _message.data {
|
|
|
|
*d += line.trim_end()
|
|
|
|
} else {
|
|
|
|
_message.data = Some(String::from(line.trim_end()))
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(None)
|
|
|
|
}
|
2023-07-02 20:18:43 +02:00
|
|
|
_ => Err(MSG_FAIL),
|
2023-07-02 20:15:10 +02:00
|
|
|
};
|
2023-07-02 19:59:25 +02:00
|
|
|
}
|
|
|
|
ConnectionState::Done => {
|
|
|
|
if line.starts_with(CMD_QUIT) {
|
|
|
|
self.finalize_mail();
|
2023-07-02 20:15:10 +02:00
|
|
|
return Ok(Some(MSG_BYE));
|
2023-07-02 19:59:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if line.starts_with(CMD_MAIL_FROM) {
|
|
|
|
self.finalize_mail();
|
2023-07-02 20:15:10 +02:00
|
|
|
return Ok(Some(MSG_OK));
|
2023-07-02 19:59:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Some(MSG_UNEXPECTED))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn finalize_mail(&mut self) {
|
2023-07-02 20:18:43 +02:00
|
|
|
let current_message = self.current_message.take();
|
2023-07-02 20:15:10 +02:00
|
|
|
|
|
|
|
if let Some(mail) = current_message {
|
|
|
|
self.messages.push(mail)
|
2023-07-02 19:59:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-02 20:18:43 +02:00
|
|
|
fn add_recipient(&mut self, line: &str) -> Result<(), &'static str> {
|
2023-07-02 19:59:25 +02:00
|
|
|
match self.current_message {
|
|
|
|
Some(ref mut message) => {
|
2023-07-02 20:18:43 +02:00
|
|
|
message.recipients.push(line.to_owned());
|
2023-07-02 19:59:25 +02:00
|
|
|
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(())
|
2023-07-02 20:18:43 +02:00
|
|
|
}
|