commit 4d2c5c777a587721fb1f91e4e00bad1940cbeebb
parent fd94cdd1382767145420bf09874fa9a201278b61
Author: alex wennerberg <alex@alexwennerberg.com>
Date: Sat, 22 May 2021 13:03:42 -0700
Clean up escape HTML interface (kinda)
Diffstat:
M | src/main.rs | | | 29 | +++++++++++++++++------------ |
M | src/utils.rs | | | 50 | ++++++++++++++++++++++++++++++-------------------- |
2 files changed, 47 insertions(+), 32 deletions(-)
diff --git a/src/main.rs b/src/main.rs
@@ -3,6 +3,7 @@ use mailparse::{parse_mail, MailHeaderMap, ParsedMail};
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
use std::path::Path;
+use utils::EscapedHTML;
mod utils;
@@ -58,23 +59,27 @@ fn parse_path(s: &std::ffi::OsStr) -> Result<std::path::PathBuf, &'static str> {
}
fn email_to_html(email: ParsedMail) -> String {
- // TODO use format strings here i think
- let escaped_header = |f: &str| {
- let mut s = String::new();
- let field = &email.headers.get_first_value(f).unwrap_or("".to_string());
- utils::escape_html(&mut s, &field);
- return s;
- };
+ // Probably if I was better at Rust I could rewrite these in a more efficient way,
+ // avoiding unnecessary allocs. Could use some of the lower-level features of
+ // the mailparse library
+ //
+ // could definitely improve the api here
+ let get_header_alloc = |f| email.headers.get_first_value(f).unwrap_or("".to_string());
return format!(
r#"
-<b>From<b>: {from}<br>
-<b>Subject</b>: {subject}
+<b>From</b>: {from}<br>
+<b>Subject</b>: {subject}<br>
+<b>Date</b>: {date}<br>
+<b>Message-Id</b>: {message_id}
<div id="body"> {body} </div>
"#,
- from = escaped_header("From"),
- subject = escaped_header("Subject"),
- body = &email.get_body().unwrap()
+ from = EscapedHTML(&get_header_alloc("from")),
+ subject = EscapedHTML(&get_header_alloc("subject")),
+ date = EscapedHTML(&get_header_alloc("date")),
+ message_id = EscapedHTML(&get_header_alloc("message-id")),
+ // TODO replace with get body raw to avoid unneeded alloc. same w/ headers
+ body = EscapedHTML(&email.get_body().unwrap_or("".to_string()))
);
}
diff --git a/src/utils.rs b/src/utils.rs
@@ -1,7 +1,10 @@
+use std::fmt;
use std::io;
use std::io::Write;
+
// Derived from https://github.com/raphlinus/pulldown-cmark/blob/master/src/escape.rs
// Don't use single quotes (') in any of my attributes
+// Homebrewing my html templating to minimize dependencies
// !!!WIP!!! -- still need to add tests, audit security, etc
const fn create_html_escape_table() -> [u8; 256] {
@@ -17,27 +20,34 @@ static HTML_ESCAPE_TABLE: [u8; 256] = create_html_escape_table();
static HTML_ESCAPES: [&str; 5] = ["", """, "&", "<", ">"];
-pub fn escape_html(w: &mut String, s: &str) {
- let bytes = s.as_bytes();
- let mut mark = 0;
- let mut i = 0;
- while i < s.len() {
- match bytes[i..]
- .iter()
- .position(|&c| HTML_ESCAPE_TABLE[c as usize] != 0)
- {
- Some(pos) => {
- i += pos;
+pub struct EscapedHTML<'a>(pub &'a str);
+
+impl fmt::Display for EscapedHTML<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let s = self.0;
+ let bytes = s.as_bytes();
+ let mut mark = 0;
+ let mut i = 0;
+ // does this work
+ while i < s.len() {
+ match bytes[i..]
+ .iter()
+ .position(|&c| HTML_ESCAPE_TABLE[c as usize] != 0)
+ {
+ Some(pos) => {
+ i += pos;
+ }
+ None => break,
}
- None => break,
+ let c = bytes[i];
+ let escape = HTML_ESCAPE_TABLE[c as usize];
+ let escape_seq = HTML_ESCAPES[escape as usize];
+ f.write_str(&s[mark..i])?;
+ f.write_str(escape_seq)?;
+ i += 1;
+ mark = i; // all escaped characters are ASCII
}
- let c = bytes[i];
- let escape = HTML_ESCAPE_TABLE[c as usize];
- let escape_seq = HTML_ESCAPES[escape as usize];
- w.push_str(&s[mark..i]);
- w.push_str(escape_seq);
- i += 1;
- mark = i; // all escaped characters are ASCII
+ f.write_str(&s[mark..])?;
+ Ok(())
}
- w.push_str(&s[mark..])
}