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"
|
||||
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",
|
||||
]
|
||||
|
|
|
@ -7,5 +7,6 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
chrono = "0.4.26"
|
||||
mailparse = "0.14.0"
|
||||
rand = "0.8.5"
|
||||
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) => {
|
||||
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, {
|
||||
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<String> = &mailbox.messages.iter().map(|msg| {
|
||||
return format!("<h3>From {}<h3><br><pre>{}</pre><br>", msg.sender, msg.data)
|
||||
let body: &Vec<String> = &mailbox.messages.iter().enumerate().map(|(i, msg)| {
|
||||
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();
|
||||
|
||||
rouille::Response::html(body.join(""))
|
||||
rouille::Response::html(format!(
|
||||
"<h1>Mailbox: {}</h1>{}<small>okay bye</small>",
|
||||
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('\'', "'")
|
||||
}
|
||||
|
|
40
src/smtp.rs
40
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,13 +94,35 @@ impl IncomingMessage {
|
|||
}
|
||||
}
|
||||
|
||||
fn finalize(self) -> Message {
|
||||
Message {
|
||||
fn parse(self) -> Option<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,
|
||||
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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@ pub struct Mailbox {
|
|||
pub struct Message {
|
||||
pub sender: String,
|
||||
pub recipients: Vec<String>,
|
||||
pub data: String,
|
||||
pub subject: String,
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
impl Mailbox {
|
||||
|
|
Loading…
Reference in New Issue