Compare commits
5 Commits
66ada655fc
...
a6f0971f0f
| Author | SHA1 | Date | |
|---|---|---|---|
| a6f0971f0f | |||
| 21789df60a | |||
| 584ff1504d | |||
| caff1a1ed3 | |||
| d7b4411017 |
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -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",
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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.))
|
||||||
]
|
]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user