Compare commits

...

5 Commits

9 changed files with 47 additions and 22 deletions

10
Cargo.lock generated
View File

@ -2469,7 +2469,7 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]] [[package]]
name = "letterbox" name = "letterbox"
version = "0.0.55" version = "0.0.56"
dependencies = [ dependencies = [
"build-info", "build-info",
"build-info-build", "build-info-build",
@ -2988,7 +2988,7 @@ dependencies = [
[[package]] [[package]]
name = "notmuch" name = "notmuch"
version = "0.0.55" version = "0.0.56"
dependencies = [ dependencies = [
"itertools 0.10.5", "itertools 0.10.5",
"log", "log",
@ -3625,7 +3625,7 @@ dependencies = [
[[package]] [[package]]
name = "procmail2notmuch" name = "procmail2notmuch"
version = "0.0.55" version = "0.0.56"
dependencies = [ dependencies = [
"anyhow", "anyhow",
] ]
@ -4547,7 +4547,7 @@ dependencies = [
[[package]] [[package]]
name = "server" name = "server"
version = "0.0.55" version = "0.0.56"
dependencies = [ dependencies = [
"ammonia", "ammonia",
"anyhow", "anyhow",
@ -4646,7 +4646,7 @@ dependencies = [
[[package]] [[package]]
name = "shared" name = "shared"
version = "0.0.55" version = "0.0.56"
dependencies = [ dependencies = [
"build-info", "build-info",
"notmuch", "notmuch",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "notmuch" name = "notmuch"
version = "0.0.55" version = "0.0.56"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,6 +1,6 @@
[package] [package]
name = "procmail2notmuch" name = "procmail2notmuch"
version = "0.0.55" version = "0.0.56"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,6 +1,6 @@
[package] [package]
name = "server" name = "server"
version = "0.0.55" version = "0.0.56"
edition = "2021" edition = "2021"
default-run = "server" default-run = "server"

View File

@ -6,7 +6,7 @@ use std::{
}; };
use log::{error, info, warn}; use log::{error, info, warn};
use mailparse::{parse_mail, MailHeader, MailHeaderMap, ParsedMail}; use mailparse::{parse_content_type, parse_mail, MailHeader, MailHeaderMap, ParsedMail};
use memmap::MmapOptions; use memmap::MmapOptions;
use notmuch::Notmuch; use notmuch::Notmuch;
@ -451,7 +451,7 @@ fn extract_alternative(m: &ParsedMail, part_addr: &mut Vec<String>) -> Result<Bo
} }
for sp in &m.subparts { for sp in &m.subparts {
if sp.ctype.mimetype.as_str() == MULTIPART_MIXED { if sp.ctype.mimetype.as_str() == MULTIPART_MIXED {
return extract_related(sp, part_addr); return extract_mixed(sp, part_addr);
} }
} }
for sp in &m.subparts { for sp in &m.subparts {
@ -480,6 +480,7 @@ fn extract_alternative(m: &ParsedMail, part_addr: &mut Vec<String>) -> Result<Bo
// multipart/mixed defines multiple types of context all of which should be presented to the user // multipart/mixed defines multiple types of context all of which should be presented to the user
// 'serially'. // 'serially'.
fn extract_mixed(m: &ParsedMail, part_addr: &mut Vec<String>) -> Result<Body, ServerError> { fn extract_mixed(m: &ParsedMail, part_addr: &mut Vec<String>) -> Result<Body, ServerError> {
//todo!("add some sort of visual indicator there are unhandled types, i.e. .ics files");
let handled_types = vec![ let handled_types = vec![
IMAGE_JPEG, IMAGE_JPEG,
IMAGE_PJPEG, IMAGE_PJPEG,
@ -532,13 +533,25 @@ fn extract_mixed(m: &ParsedMail, part_addr: &mut Vec<String>) -> Result<Body, Se
))); )));
} }
} }
_ => (), mt => parts.push(unhandled_html(MULTIPART_MIXED, mt)),
} }
part_addr.pop(); part_addr.pop();
} }
Ok(flatten_body_parts(&parts)) Ok(flatten_body_parts(&parts))
} }
fn unhandled_html(parent_type: &str, child_type: &str) -> Body {
Body::Html(Html {
html: format!(
r#"
<div class="p-4 error">
Unhandled mimetype {child_type} in a {parent_type} message
</div>
"#
),
content_tree: String::new(),
})
}
fn flatten_body_parts(parts: &[Body]) -> Body { fn flatten_body_parts(parts: &[Body]) -> Body {
let html = parts let html = parts
.iter() .iter()
@ -663,6 +676,7 @@ fn walk_attachments_inner<T, F: Fn(&ParsedMail, &[usize]) -> Option<T> + Copy>(
fn extract_attachments(m: &ParsedMail, id: &str) -> Result<Vec<Attachment>, ServerError> { fn extract_attachments(m: &ParsedMail, id: &str) -> Result<Vec<Attachment>, ServerError> {
let mut attachments = Vec::new(); let mut attachments = Vec::new();
for (idx, sp) in m.subparts.iter().enumerate() { for (idx, sp) in m.subparts.iter().enumerate() {
info!("sp: {:?}", sp.headers);
if let Some(attachment) = extract_attachment(sp, id, &[idx]) { if let Some(attachment) = extract_attachment(sp, id, &[idx]) {
// Filter out inline attachements, they're flattened into the body of the message. // Filter out inline attachements, they're flattened into the body of the message.
if attachment.disposition == DispositionType::Attachment { if attachment.disposition == DispositionType::Attachment {
@ -675,11 +689,22 @@ fn extract_attachments(m: &ParsedMail, id: &str) -> Result<Vec<Attachment>, Serv
fn extract_attachment(m: &ParsedMail, id: &str, idx: &[usize]) -> Option<Attachment> { fn extract_attachment(m: &ParsedMail, id: &str, idx: &[usize]) -> Option<Attachment> {
let pcd = m.get_content_disposition(); let pcd = m.get_content_disposition();
// TODO: do we need to handle empty filename attachments, or should we change the definition of let pct = m
// Attachment::filename? .get_headers()
let Some(filename) = pcd.params.get("filename").map(|f| f.clone()) else { .get_first_value("Content-Type")
return None; .map(|s| parse_content_type(&s));
let filename = match (
pcd.params.get("filename").map(|f| f.clone()),
pct.map(|pct| pct.params.get("name").map(|f| f.clone())),
) {
// Use filename from Content-Disposition
(Some(filename), _) => filename,
// Use filename from Content-Type
(_, Some(Some(name))) => name,
// No known filename, assume it's not an attachment
_ => return None,
}; };
info!("filename {filename}");
// TODO: grab this from somewhere // TODO: grab this from somewhere
let content_id = None; let content_id = None;

View File

@ -1,6 +1,6 @@
[package] [package]
name = "shared" name = "shared"
version = "0.0.55" version = "0.0.56"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,5 +1,5 @@
[package] [package]
version = "0.0.55" version = "0.0.56"
name = "letterbox" name = "letterbox"
repository = "https://github.com/seed-rs/seed-quickstart" repository = "https://github.com/seed-rs/seed-quickstart"
authors = ["Bill Thiede <git@xinu.tv>"] authors = ["Bill Thiede <git@xinu.tv>"]
@ -7,7 +7,7 @@ description = "App Description"
categories = ["category"] categories = ["category"]
license = "MIT" license = "MIT"
readme = "./README.md" readme = "./README.md"
edition = "2018" edition = "2021"
[build-dependencies] [build-dependencies]
build-info-build = "0.0.38" build-info-build = "0.0.38"

View File

@ -1,5 +1,5 @@
fn main() { fn main() {
// Calling `build_info_build::build_script` collects all data and makes it available to `build_info::build_info!` // Calling `build_info_build::build_script` collects all data and makes it available to `build_info::build_info!`
// and `build_info::format!` in the main program. // and `build_info::format!` in the main program.
build_info_build::build_script(); build_info_build::build_script();
} }

View File

@ -1157,7 +1157,7 @@ fn click_to_top() -> Node<Msg> {
C!["button", "is-danger", "is-small"], C!["button", "is-danger", "is-small"],
span!["Top"], span!["Top"],
span![C!["icon"], i![C!["fas", "fa-arrow-turn-up"]]], span![C!["icon"], i![C!["fas", "fa-arrow-turn-up"]]],
ev(Ev::Click, move |_| web_sys::window() ev(Ev::Click, |_| web_sys::window()
.unwrap() .unwrap()
.scroll_to_with_x_and_y(0., 0.)) .scroll_to_with_x_and_y(0., 0.))
] ]