diff --git a/Cargo.lock b/Cargo.lock
index 59e6439..219ed7b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -111,6 +111,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "charset"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18e9079d1a12a2cc2bffb5db039c43661836ead4082120d5844f02555aca2d46"
+dependencies = [
+ "base64",
+ "encoding_rs",
+]
+
[[package]]
name = "chrono"
version = "0.4.26"
@@ -147,6 +157,12 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "data-encoding"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
+
[[package]]
name = "deflate"
version = "1.0.0"
@@ -157,6 +173,15 @@ dependencies = [
"gzip-header",
]
+[[package]]
+name = "encoding_rs"
+version = "0.8.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "errno"
version = "0.3.1"
@@ -332,6 +357,17 @@ version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+[[package]]
+name = "mailparse"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b56570f5f8c0047260d1c8b5b331f62eb9c660b9dd4071a8c46f8c7d3f280aa"
+dependencies = [
+ "charset",
+ "data-encoding",
+ "quoted_printable",
+]
+
[[package]]
name = "memchr"
version = "2.5.0"
@@ -442,6 +478,12 @@ dependencies = [
"proc-macro2",
]
+[[package]]
+name = "quoted_printable"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3866219251662ec3b26fc217e3e05bf9c4f84325234dfb96bf0bf840889e49"
+
[[package]]
name = "rand"
version = "0.8.5"
@@ -669,6 +711,7 @@ name = "tmpmail"
version = "0.1.0"
dependencies = [
"chrono",
+ "mailparse",
"rand",
"rouille",
]
diff --git a/Cargo.toml b/Cargo.toml
index 9790ac7..453fcc6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,5 +7,6 @@ edition = "2021"
[dependencies]
chrono = "0.4.26"
+mailparse = "0.14.0"
rand = "0.8.5"
rouille = "3.6.2"
diff --git a/src/http.rs b/src/http.rs
index f0da425..c817096 100644
--- a/src/http.rs
+++ b/src/http.rs
@@ -9,10 +9,22 @@ pub fn http_handler(request: &Request, state: &State) -> Response {
},
(GET) (/ffff) => {
- rouille::Response::html("
email stuff
")
+ rouille::Response::redirect_302("/ffff/")
},
- (POST) (/ffff) => {
+ (GET) (/ffff/) => {
+ rouille::Response::html("
+ email stuff
+
+ ")
+ },
+
+ (POST) (/ffff/) => {
let data = try_or_400!(post_input!(request, {
domain: String
}));
@@ -23,23 +35,49 @@ pub fn http_handler(request: &Request, state: &State) -> Response {
state.lock().unwrap().insert(email.clone(), Mailbox::new(email.clone()));
- rouille::Response::text(email)
+ rouille::Response::redirect_301(format!("{}/", email))
},
- (GET) (/ffff/{email: String}) => {
+ (GET) (/ffff/{email: String}/) => {
println!("{}", email);
if let Some(mailbox) = state.lock().unwrap().get(&email) {
- let body: &Vec = &mailbox.messages.iter().map(|msg| {
- return format!("From {}
{}
", msg.sender, msg.data)
+ let body: &Vec = &mailbox.messages.iter().enumerate().map(|(i, msg)| {
+ format!(
+ "\"{}\" from {}
",
+ escape(&msg.subject),
+ escape(&msg.sender),
+ i
+ )
}).collect();
- rouille::Response::html(body.join(""))
+ rouille::Response::html(format!(
+ "Mailbox: {}
{}okay bye",
+ escape(&mailbox.email),
+ body.join("")
+ ))
} else {
rouille::Response::text("Mailbox not found")
}
},
+ (GET) (/ffff/{email: String}/{msg: usize}) => {
+ if let Some(mailbox) = state.lock().unwrap().get(&email) {
+ if let Some(mail) = mailbox.messages.get(msg) {
+ return rouille::Response::html(&mail.body)
+ }
+ }
+
+ rouille::Response::text("Message not found")
+ },
+
_ => rouille::Response::empty_404()
)
}
+
+fn escape(data: &str) -> String {
+ data.replace('>', ">")
+ .replace('<', "<")
+ .replace('\"', """)
+ .replace('\'', "'")
+}
diff --git a/src/smtp.rs b/src/smtp.rs
index b3405bc..21bb29b 100644
--- a/src/smtp.rs
+++ b/src/smtp.rs
@@ -1,6 +1,7 @@
use std::io::{BufRead, BufReader, Write};
use crate::state::{Message, State};
+use mailparse::{parse_mail, MailHeaderMap, ParsedMail};
use std::net::{TcpListener, TcpStream};
pub fn start_server(state: State) {
@@ -20,10 +21,11 @@ pub fn start_server(state: State) {
} else if let Ok(mut state_inner) = state.lock() {
for mail in connection.messages {
println!(
- "Mail from {} for {}\n{}",
+ "Mail \"{}\" from {} for {}\n{}",
+ mail.subject,
mail.sender,
mail.recipients.join(", "),
- mail.data
+ mail.body
);
for r in &mail.recipients {
@@ -92,15 +94,37 @@ impl IncomingMessage {
}
}
- fn finalize(self) -> Message {
- Message {
+ fn parse(self) -> Option {
+ let data = self.data?;
+ let mail = parse_mail(data.as_bytes()).ok()?;
+
+ let body_part = part_by_content_type(&mail, "text/html")
+ .or_else(|| part_by_content_type(&mail, "text/plain"));
+
+ let body = match body_part {
+ Some(c) => c.get_body().ok()?,
+ None => mail.get_body().unwrap_or("".to_owned()),
+ };
+
+ Some(Message {
+ body,
sender: self.sender,
recipients: self.recipients,
- data: self.data.unwrap_or("".to_string()),
- }
+ subject: mail
+ .get_headers()
+ .get_first_value("Subject")
+ .unwrap_or("".to_string()),
+ })
}
}
+fn part_by_content_type<'a>(
+ mail: &'a ParsedMail,
+ content_type: &str,
+) -> Option<&'a ParsedMail<'a>> {
+ mail.parts().find(|p| p.ctype.mimetype == content_type)
+}
+
impl Connection {
fn new() -> Self {
Connection {
@@ -126,7 +150,7 @@ impl Connection {
return Err(ERR_READ_FAILED);
}
- println!("Read line: {}", line);
+ println!("Read line: {}", line.trim_end());
let response = self.handle_line(&line);
@@ -216,10 +240,10 @@ impl Connection {
}
fn finalize_mail(&mut self) {
- let current_message = self.current_message.take();
+ let current_message = self.current_message.take().and_then(IncomingMessage::parse);
if let Some(mail) = current_message {
- self.messages.push(mail.finalize())
+ self.messages.push(mail);
}
}
diff --git a/src/state.rs b/src/state.rs
index e39dadb..4c463e9 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -18,7 +18,8 @@ pub struct Mailbox {
pub struct Message {
pub sender: String,
pub recipients: Vec,
- pub data: String,
+ pub subject: String,
+ pub body: String,
}
impl Mailbox {