Compare commits

..

No commits in common. "e8c58bdbd0b6db02d703e446977b602c76b436da" and "1261bdf8a9166e67d9cb84a07ad0785e2e5350c0" have entirely different histories.

6 changed files with 18 additions and 173 deletions

66
Cargo.lock generated
View File

@ -36,19 +36,6 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "ammonia"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e6d1c7838db705c9b756557ee27c384ce695a1c51a6fe528784cb1c6840170"
dependencies = [
"html5ever 0.26.0",
"maplit",
"once_cell",
"tendril",
"url 2.4.1",
]
[[package]] [[package]]
name = "android-tzdata" name = "android-tzdata"
version = "0.1.1" version = "0.1.1"
@ -1274,21 +1261,7 @@ checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148"
dependencies = [ dependencies = [
"log 0.4.20", "log 0.4.20",
"mac", "mac",
"markup5ever 0.10.1", "markup5ever",
"proc-macro2 1.0.66",
"quote 1.0.33",
"syn 1.0.109",
]
[[package]]
name = "html5ever"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
dependencies = [
"log 0.4.20",
"mac",
"markup5ever 0.11.0",
"proc-macro2 1.0.66", "proc-macro2 1.0.66",
"quote 1.0.33", "quote 1.0.33",
"syn 1.0.109", "syn 1.0.109",
@ -1550,7 +1523,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358" checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358"
dependencies = [ dependencies = [
"cssparser 0.27.2", "cssparser 0.27.2",
"html5ever 0.25.2", "html5ever",
"matches", "matches",
"selectors", "selectors",
] ]
@ -1664,12 +1637,6 @@ dependencies = [
"quoted_printable", "quoted_printable",
] ]
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]] [[package]]
name = "markup5ever" name = "markup5ever"
version = "0.10.1" version = "0.10.1"
@ -1678,21 +1645,7 @@ checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
dependencies = [ dependencies = [
"log 0.4.20", "log 0.4.20",
"phf 0.8.0", "phf 0.8.0",
"phf_codegen 0.8.0", "phf_codegen",
"string_cache",
"string_cache_codegen",
"tendril",
]
[[package]]
name = "markup5ever"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
dependencies = [
"log 0.4.20",
"phf 0.10.1",
"phf_codegen 0.10.0",
"string_cache", "string_cache",
"string_cache_codegen", "string_cache_codegen",
"tendril", "tendril",
@ -2128,16 +2081,6 @@ dependencies = [
"phf_shared 0.8.0", "phf_shared 0.8.0",
] ]
[[package]]
name = "phf_codegen"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
dependencies = [
"phf_generator 0.10.0",
"phf_shared 0.10.0",
]
[[package]] [[package]]
name = "phf_generator" name = "phf_generator"
version = "0.8.0" version = "0.8.0"
@ -2824,7 +2767,7 @@ dependencies = [
"log 0.4.20", "log 0.4.20",
"matches", "matches",
"phf 0.8.0", "phf 0.8.0",
"phf_codegen 0.8.0", "phf_codegen",
"precomputed-hash", "precomputed-hash",
"servo_arc", "servo_arc",
"smallvec", "smallvec",
@ -2893,7 +2836,6 @@ dependencies = [
name = "server" name = "server"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ammonia",
"async-graphql", "async-graphql",
"async-graphql-rocket", "async-graphql-rocket",
"glog", "glog",

View File

@ -23,7 +23,6 @@ rocket_cors = "0.6.0"
rayon = "1.8.0" rayon = "1.8.0"
memmap = "0.7.0" memmap = "0.7.0"
mailparse = "0.14.0" mailparse = "0.14.0"
ammonia = "3.3.0"
[dependencies.rocket_contrib] [dependencies.rocket_contrib]
version = "0.4.11" version = "0.4.11"

View File

@ -8,7 +8,7 @@ use async_graphql::{
Context, EmptyMutation, EmptySubscription, Error, FieldResult, Object, Schema, SimpleObject, Context, EmptyMutation, EmptySubscription, Error, FieldResult, Object, Schema, SimpleObject,
Union, Union,
}; };
use log::{error, info, warn}; use log::{info, warn};
use mailparse::{parse_mail, MailHeaderMap, ParsedMail}; use mailparse::{parse_mail, MailHeaderMap, ParsedMail};
use memmap::MmapOptions; use memmap::MmapOptions;
use notmuch::Notmuch; use notmuch::Notmuch;
@ -58,8 +58,6 @@ pub struct Message {
pub timestamp: Option<i64>, pub timestamp: Option<i64>,
// The body contents // The body contents
pub body: Body, pub body: Body,
// On disk location of message
pub path: String,
} }
#[derive(Debug)] #[derive(Debug)]
@ -243,11 +241,18 @@ impl QueryRoot {
.headers .headers
.get_first_value("date") .get_first_value("date")
.and_then(|d| mailparse::dateparse(&d).ok()); .and_then(|d| mailparse::dateparse(&d).ok());
let body = match extract_body(&m)? { let body = m.get_body()?;
Body::Html(Html { html }) => Body::Html(Html { let body = match m.ctype.mimetype.as_str() {
html: ammonia::clean(&html), "text/plain" => Body::PlainText(PlainText { text: body }),
}), "text/html" => Body::Html(Html { html: body }),
b => b, _ => {
let msg = format!(
"Unhandled body content type:\n{}",
render_content_type_tree(&m)
);
warn!("{}", msg);
Body::UnhandledContentType(UnhandledContentType { text: msg })
}
}; };
messages.push(Message { messages.push(Message {
from, from,
@ -256,7 +261,6 @@ impl QueryRoot {
subject, subject,
timestamp, timestamp,
body, body,
path,
}); });
} }
messages.reverse(); messages.reverse();
@ -272,92 +276,12 @@ impl QueryRoot {
} }
} }
fn extract_body(m: &ParsedMail) -> Result<Body, Error> {
let body = m.get_body()?;
let ret = match m.ctype.mimetype.as_str() {
"text/plain" => return Ok(Body::PlainText(PlainText { text: body })),
"text/html" => return Ok(Body::Html(Html { html: body })),
"multipart/mixed" => extract_mixed(m),
"multipart/alternative" => extract_alternative(m),
_ => extract_unhandled(m),
};
if let Err(err) = ret {
error!("Failed to extract body: {err:?}");
return Ok(extract_unhandled(m)?);
}
ret
}
fn extract_unhandled(m: &ParsedMail) -> Result<Body, Error> {
let msg = format!(
"Unhandled body content type:\n{}",
render_content_type_tree(m)
);
warn!("{}", msg);
Ok(Body::UnhandledContentType(UnhandledContentType {
text: msg,
}))
}
fn extract_alternative(m: &ParsedMail) -> Result<Body, Error> {
for sp in &m.subparts {
if sp.ctype.mimetype == "text/html" {
let body = sp.get_body()?;
return Ok(Body::Html(Html { html: body }));
}
}
for sp in &m.subparts {
if sp.ctype.mimetype == "text/plain" {
let body = sp.get_body()?;
return Ok(Body::PlainText(PlainText { text: body }));
}
}
Err("extract_alternative".into())
}
fn extract_mixed(m: &ParsedMail) -> Result<Body, Error> {
for sp in &m.subparts {
if sp.ctype.mimetype == "multipart/alternative" {
return extract_alternative(sp);
}
}
for sp in &m.subparts {
if sp.ctype.mimetype == "multipart/related" {
return extract_related(sp);
}
}
for sp in &m.subparts {
let body = sp.get_body()?;
match sp.ctype.mimetype.as_str() {
"text/plain" => return Ok(Body::PlainText(PlainText { text: body })),
"text/html" => return Ok(Body::Html(Html { html: body })),
_ => (),
}
}
Err("extract_mixed".into())
}
fn extract_related(m: &ParsedMail) -> Result<Body, Error> {
// TODO(wathiede): collect related things and change return type to new Body arm.
for sp in &m.subparts {
if sp.ctype.mimetype == "text/html" {
let body = sp.get_body()?;
return Ok(Body::Html(Html { html: body }));
}
}
for sp in &m.subparts {
if sp.ctype.mimetype == "text/plain" {
let body = sp.get_body()?;
return Ok(Body::PlainText(PlainText { text: body }));
}
}
Err("extract_related".into())
}
fn render_content_type_tree(m: &ParsedMail) -> String { fn render_content_type_tree(m: &ParsedMail) -> String {
const WIDTH: usize = 4; const WIDTH: usize = 4;
fn render_rec(m: &ParsedMail, depth: usize) -> String { fn render_rec(m: &ParsedMail, depth: usize) -> String {
let mut parts = Vec::new(); let mut parts = Vec::new();
let msg = format!("{} {}", "-".repeat(depth * WIDTH), m.ctype.mimetype); let msg = format!("{} {}", "-".repeat(depth * WIDTH), m.ctype.mimetype);
println!("{msg}",);
parts.push(msg); parts.push(msg);
if !m.ctype.charset.is_empty() { if !m.ctype.charset.is_empty() {
parts.push(format!( parts.push(format!(

View File

@ -290,22 +290,6 @@
"ofType": null "ofType": null
} }
} }
},
{
"args": [],
"deprecationReason": null,
"description": null,
"isDeprecated": false,
"name": "path",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
} }
], ],
"inputFields": null, "inputFields": null,

View File

@ -28,7 +28,6 @@ query ShowThreadQuery($threadId: String!) {
contents contents
} }
} }
path
} }
} }
tags { tags {

View File

@ -124,15 +124,12 @@ input::placeholder, .input::placeholder{
float: right; float: right;
} }
/* Hide quoted emails */ /* Hide quoted emails */
/*
div[name="quote"], div[name="quote"],
blockquote[type="cite"], blockquote[type="cite"],
.gmail_quote { .gmail_quote {
background-color: red; background-color: red;
display: none; display: none;
} }
*/
.desktop-main-content { .desktop-main-content {
display: grid; display: grid;
grid-template-columns: 12rem 1fr; grid-template-columns: 12rem 1fr;