Compare commits
No commits in common. "e8c58bdbd0b6db02d703e446977b602c76b436da" and "1261bdf8a9166e67d9cb84a07ad0785e2e5350c0" have entirely different histories.
e8c58bdbd0
...
1261bdf8a9
66
Cargo.lock
generated
66
Cargo.lock
generated
@ -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",
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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!(
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -28,7 +28,6 @@ query ShowThreadQuery($threadId: String!) {
|
|||||||
contents
|
contents
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
path
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tags {
|
tags {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user