Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
psarna committed Feb 28, 2023
0 parents commit 44b11f2
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
Cargo.lock
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "eatmail"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tokio = { version = "1.25.0", features = ["full"] }
89 changes: 89 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;

use std::env;
use std::error::Error;

// FIXME: wrap in a struct implementing a state machine
async fn handle_smtp(msg: &str) -> &'static [u8] {
println!("Received {msg}");
let mut msg = msg.split_whitespace();
let command = msg.next().expect("empty message").to_lowercase();
match command.as_str() {
"ehlo" | "helo" => b"250\n",
"mail" => {
println!(
"{}",
msg.map(|s| s.to_string())
.collect::<Vec<String>>()
.join(" ")
);
b"250\n"
}
"rcpt" => {
println!(
"{}",
msg.map(|s| s.to_string())
.collect::<Vec<String>>()
.join(" ")
);
b"250\n"
}
"data" => b"354\n",
"quit" => b"221\n",
_ => b"250\n",
}
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let addr = env::args()
.nth(1)
.unwrap_or_else(|| "127.0.0.1:8080".to_string());

let listener = TcpListener::bind(&addr).await?;
println!("Listening on: {}", addr);

loop {
// Asynchronously wait for an inbound socket.
let (mut socket, _) = listener.accept().await?;

tokio::spawn(async move {
println!("Accepted");
socket
.write_all(b"220 eatmail\n")
.await
.expect("failed to introduce myself");
let mut buf = vec![0; 65536];

// In a loop, read data from the socket and write the data back.
loop {
let n = socket
.read(&mut buf)
.await
.expect("failed to read data from socket");

if n == 0 {
println!("Received \\0");
return;
}
let msg = match std::str::from_utf8(&buf) {
Ok(msg) => msg,
Err(e) => {
println!("Unexpected response: {e}");
break;
}
};
let response = handle_smtp(msg).await;
socket
.write_all(response)
.await
.expect("failed to write the response");
if response == b"221\n" {
// fixme with enum
break;
}
}
});
}
}
13 changes: 13 additions & 0 deletions test/payload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import smtplib

fromaddr = "[email protected]"
toaddr = "[email protected]"

# Add the From: and To: headers at the start!
msg = f"From: {fromaddr}\r\nTo: {toaddr}\r\n\r\n"
msg += "test \nmail\n goodbye\n"

server = smtplib.SMTP('localhost', port=8080)
server.set_debuglevel(1)
server.sendmail(fromaddr, toaddr, msg)
server.quit()

0 comments on commit 44b11f2

Please sign in to comment.