Show more data on webinterface
This commit is contained in:
parent
e0fd858c2f
commit
6f83f0a62f
|
@ -111,6 +111,16 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
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]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.26"
|
version = "0.4.26"
|
||||||
|
@ -147,6 +157,12 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-encoding"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deflate"
|
name = "deflate"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -157,6 +173,15 @@ dependencies = [
|
||||||
"gzip-header",
|
"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]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -332,6 +357,17 @@ version = "0.4.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
|
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]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
@ -442,6 +478,12 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quoted_printable"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a3866219251662ec3b26fc217e3e05bf9c4f84325234dfb96bf0bf840889e49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
@ -669,6 +711,7 @@ name = "tmpmail"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"mailparse",
|
||||||
"rand",
|
"rand",
|
||||||
"rouille",
|
"rouille",
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,5 +7,6 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.26"
|
chrono = "0.4.26"
|
||||||
|
mailparse = "0.14.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rouille = "3.6.2"
|
rouille = "3.6.2"
|
||||||
|
|
52
src/http.rs
52
src/http.rs
|
@ -9,10 +9,22 @@ pub fn http_handler(request: &Request, state: &State) -> Response {
|
||||||
},
|
},
|
||||||
|
|
||||||
(GET) (/ffff) => {
|
(GET) (/ffff) => {
|
||||||
rouille::Response::html("<h1>email stuff</h1>")
|
rouille::Response::redirect_302("/ffff/")
|
||||||
},
|
},
|
||||||
|
|
||||||
(POST) (/ffff) => {
|
(GET) (/ffff/) => {
|
||||||
|
rouille::Response::html("
|
||||||
|
<h1>email stuff</h1>
|
||||||
|
<form method='POST'>
|
||||||
|
<select name='domain'>
|
||||||
|
<option value='test.m5w.de'>test.m5w.de</option>
|
||||||
|
</select>
|
||||||
|
<input type='submit'>
|
||||||
|
</form>
|
||||||
|
")
|
||||||
|
},
|
||||||
|
|
||||||
|
(POST) (/ffff/) => {
|
||||||
let data = try_or_400!(post_input!(request, {
|
let data = try_or_400!(post_input!(request, {
|
||||||
domain: String
|
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()));
|
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);
|
println!("{}", email);
|
||||||
|
|
||||||
if let Some(mailbox) = state.lock().unwrap().get(&email) {
|
if let Some(mailbox) = state.lock().unwrap().get(&email) {
|
||||||
let body: &Vec<String> = &mailbox.messages.iter().map(|msg| {
|
let body: &Vec<String> = &mailbox.messages.iter().enumerate().map(|(i, msg)| {
|
||||||
return format!("<h3>From {}<h3><br><pre>{}</pre><br>", msg.sender, msg.data)
|
format!(
|
||||||
|
"<h3>\"{}\" from {}</h3><br><iframe src=\"{}\" sandbox csp=\"default-src 'none'; image-src data:\"></iframe><br>",
|
||||||
|
escape(&msg.subject),
|
||||||
|
escape(&msg.sender),
|
||||||
|
i
|
||||||
|
)
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
rouille::Response::html(body.join(""))
|
rouille::Response::html(format!(
|
||||||
|
"<h1>Mailbox: {}</h1>{}<small>okay bye</small>",
|
||||||
|
escape(&mailbox.email),
|
||||||
|
body.join("")
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
rouille::Response::text("Mailbox not found")
|
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()
|
_ => rouille::Response::empty_404()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn escape(data: &str) -> String {
|
||||||
|
data.replace('>', ">")
|
||||||
|
.replace('<', "<")
|
||||||
|
.replace('\"', """)
|
||||||
|
.replace('\'', "'")
|
||||||
|
}
|
||||||
|
|
40
src/smtp.rs
40
src/smtp.rs
|
@ -1,6 +1,7 @@
|
||||||
use std::io::{BufRead, BufReader, Write};
|
use std::io::{BufRead, BufReader, Write};
|
||||||
|
|
||||||
use crate::state::{Message, State};
|
use crate::state::{Message, State};
|
||||||
|
use mailparse::{parse_mail, MailHeaderMap, ParsedMail};
|
||||||
use std::net::{TcpListener, TcpStream};
|
use std::net::{TcpListener, TcpStream};
|
||||||
|
|
||||||
pub fn start_server(state: State) {
|
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() {
|
} else if let Ok(mut state_inner) = state.lock() {
|
||||||
for mail in connection.messages {
|
for mail in connection.messages {
|
||||||
println!(
|
println!(
|
||||||
"Mail from {} for {}\n{}",
|
"Mail \"{}\" from {} for {}\n{}",
|
||||||
|
mail.subject,
|
||||||
mail.sender,
|
mail.sender,
|
||||||
mail.recipients.join(", "),
|
mail.recipients.join(", "),
|
||||||
mail.data
|
mail.body
|
||||||
);
|
);
|
||||||
|
|
||||||
for r in &mail.recipients {
|
for r in &mail.recipients {
|
||||||
|
@ -92,13 +94,35 @@ impl IncomingMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize(self) -> Message {
|
fn parse(self) -> Option<Message> {
|
||||||
Message {
|
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("<empty body>".to_owned()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Message {
|
||||||
|
body,
|
||||||
sender: self.sender,
|
sender: self.sender,
|
||||||
recipients: self.recipients,
|
recipients: self.recipients,
|
||||||
data: self.data.unwrap_or("<empty>".to_string()),
|
subject: mail
|
||||||
|
.get_headers()
|
||||||
|
.get_first_value("Subject")
|
||||||
|
.unwrap_or("<empty subject>".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 {
|
impl Connection {
|
||||||
|
@ -126,7 +150,7 @@ impl Connection {
|
||||||
return Err(ERR_READ_FAILED);
|
return Err(ERR_READ_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Read line: {}", line);
|
println!("Read line: {}", line.trim_end());
|
||||||
|
|
||||||
let response = self.handle_line(&line);
|
let response = self.handle_line(&line);
|
||||||
|
|
||||||
|
@ -216,10 +240,10 @@ impl Connection {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize_mail(&mut self) {
|
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 {
|
if let Some(mail) = current_message {
|
||||||
self.messages.push(mail.finalize())
|
self.messages.push(mail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,8 @@ pub struct Mailbox {
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub sender: String,
|
pub sender: String,
|
||||||
pub recipients: Vec<String>,
|
pub recipients: Vec<String>,
|
||||||
pub data: String,
|
pub subject: String,
|
||||||
|
pub body: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mailbox {
|
impl Mailbox {
|
||||||
|
|
Loading…
Reference in New Issue