commit ae87e2110631fdee6b12c0022362c98882206847
parent 88873d16954536a1c08484b23ab81cde452dc8ab
Author: alex wennerberg <alex@alexwennerberg.com>
Date: Sat, 8 Jan 2022 10:51:13 -0800
Include raw message, improve style
Diffstat:
4 files changed, 69 insertions(+), 10 deletions(-)
diff --git a/src/arg.rs b/src/arg.rs
@@ -21,6 +21,7 @@ fn usage() -> ! {
"usage: {} [maildir root]
FLAGS
-r use relative timestamps
+-R include raw emails
ARGS:
-c config file (crabmail.conf)
@@ -61,7 +62,7 @@ impl Args {
chars.for_each(|m| match m {
'c' => config = parsenext(args.next()),
'd' => out_dir = parsenext(args.next()),
- 'r' => flags.push(m),
+ 'r' | 'R' => flags.push(m),
_ => {
usage();
}
diff --git a/src/config.rs b/src/config.rs
@@ -11,6 +11,7 @@ pub struct Config {
pub base_url: String,
pub out_dir: PathBuf,
pub relative_times: bool,
+ pub include_raw: bool,
}
// TODO list-specific config
@@ -49,6 +50,7 @@ impl Config {
base_url,
out_dir: PathBuf::from(""),
relative_times: false,
+ include_raw: false,
})
}
}
diff --git a/src/main.rs b/src/main.rs
@@ -294,11 +294,12 @@ impl<'a> MailThread<'a> {
}
} div {
@ for message in self.messages.iter() {
- hr;
div(id=&message.id, class="message") {
+ div(class="message-meta") {
span(class="bold") {
: &message.subject
}
+
@ if message.in_reply_to.is_some() { // TODO figure out match
: " ";
a(title="replies-to", href=format!("#{}", message.in_reply_to.clone().unwrap())){
@@ -321,7 +322,17 @@ impl<'a> MailThread<'a> {
: " (converted from html)";
}
}
- br; br;
+ br;
+ a (class="bold", href=message.mailto(&root.subject, &self.list_name)) {
+ :"✉️ Reply"
+ }
+ @ if Config::global().include_raw {
+ : " [";
+ a(href=format!("../messages/{}", message.id)) {
+ : "Download" ;
+ }
+ : "]";
+ }} br;
@ if message.subject.starts_with("[PATCH") {
div(class="email-body monospace") {
: Raw(utils::email_body(&message.body))
@@ -331,12 +342,6 @@ impl<'a> MailThread<'a> {
: Raw(utils::email_body(&message.body))
}
}
- br;
- div(class="bold"){
- a (href=message.mailto(&root.subject, &self.list_name)) {
- :"✉️ Reply"
- }
- }
}
}
}
@@ -390,6 +395,39 @@ impl Email {
}
}
+// TODO maybe implement this.
+const EXPORT_HEADERS: &[&str] = &[
+ "Date",
+ "Subject",
+ "From",
+ "Sender",
+ "Reply-To",
+ "To",
+ "Cc",
+ "Bcc",
+ "Message-Id",
+ "In-Reply-To",
+ "References",
+ "MIME-Version",
+ "Content-Type",
+ "Content-Disposition",
+ "Content-Transfer-Encoding",
+];
+
+fn write_parsed_mail(parsed_mail: &ParsedMail, f: &mut std::fs::File) -> Result<()> {
+ for header in parsed_mail.get_headers() {
+ // binary search?
+ if EXPORT_HEADERS.contains(&header.get_key().as_str()) {
+ f.write_all(header.get_key_raw())?;
+ f.write_all(b": ")?;
+ f.write_all(header.get_value_raw())?;
+ f.write_all(b"\r\n")?;
+ }
+ }
+ f.write_all(&parsed_mail.get_body_raw()?)?;
+ Ok(())
+}
+
fn local_parse_email(parsed_mail: &ParsedMail) -> Result<Email> {
let mut body: String = "[Message has no body]".to_owned();
let mut mime: String = "".to_owned();
@@ -512,6 +550,7 @@ fn main() -> Result<()> {
let mut config = Config::from_file(&args.config).unwrap(); // TODO better err handling
config.out_dir = args.out_dir;
config.relative_times = args.flags.contains('r');
+ config.include_raw = args.flags.contains('R');
INSTANCE.set(config).unwrap();
// let is_subfolder = std::fs::read_dir(&args.maildir)
@@ -525,11 +564,14 @@ fn main() -> Result<()> {
let dirreader = Maildir::from(maildir.path().to_str().unwrap());
let file_name = maildir.file_name();
let out_dir = &Config::global().out_dir.join(&file_name);
+ std::fs::create_dir(&out_dir).ok();
let list_name = file_name.into_string().unwrap();
// filter out maildir internal junk
if list_name.as_bytes()[0] == b'.' || ["cur", "new", "tmp"].contains(&list_name.as_str()) {
continue;
}
+ let path = out_dir.join("messages");
+ std::fs::remove_dir_all(&path).ok();
names.push(list_name.clone());
// new world WIP
// let mut threader = threading::Arena::default();
@@ -546,6 +588,7 @@ fn main() -> Result<()> {
for entry in dirreader.list_cur().chain(dirreader.list_new()) {
let mut tmp = entry.unwrap();
let buffer = tmp.parsed()?;
+ // persist raw messages
let email = match local_parse_email(&buffer) {
Ok(e) => e,
Err(e) => {
@@ -553,6 +596,13 @@ fn main() -> Result<()> {
continue;
}
};
+ // write raw emails
+ if Config::global().include_raw {
+ // inefficient here -- no diff
+ std::fs::create_dir(&path).ok();
+ let mut file = File::create(out_dir.join("messages").join(email.hash()))?;
+ write_parsed_mail(&buffer, &mut file)?;
+ }
// TODO fix borrow checker
if let Some(reply) = email.in_reply_to.clone() {
match thread_index.get(&reply) {
@@ -608,7 +658,6 @@ fn main() -> Result<()> {
return None;
})
.collect();
- std::fs::create_dir(&out_dir).ok();
let thread_dir = out_dir.join("threads");
std::fs::create_dir_all(&thread_dir).ok();
let mut threads = vec![];
diff --git a/src/style.css b/src/style.css
@@ -3,6 +3,7 @@
--light-text: #CCC;
--link: #21e6c1;
--background: #1b262c;
+ --emph-background: black;
}
@media (prefers-color-scheme: light) {
:root {
@@ -10,6 +11,7 @@
--background: white;
--main-text: black;
--light-text: dimgrey;
+ --emph-background: lightgrey;
}
}
@@ -36,6 +38,11 @@ table { border-spacing: 0.5em 0.1em; }
margin-bottom: 12px;
}
+.message-meta {
+ background-color: var(--emph-background);
+ padding: 3px;
+}
+
.monospace {
font-family: monospace;
}