web & server: add handling for google calendar and wellsfargo emails.
This commit is contained in:
parent
1261bdf8a9
commit
c8147ded60
@ -8,7 +8,7 @@ use async_graphql::{
|
||||
Context, EmptyMutation, EmptySubscription, Error, FieldResult, Object, Schema, SimpleObject,
|
||||
Union,
|
||||
};
|
||||
use log::{info, warn};
|
||||
use log::{error, info, warn};
|
||||
use mailparse::{parse_mail, MailHeaderMap, ParsedMail};
|
||||
use memmap::MmapOptions;
|
||||
use notmuch::Notmuch;
|
||||
@ -58,6 +58,8 @@ pub struct Message {
|
||||
pub timestamp: Option<i64>,
|
||||
// The body contents
|
||||
pub body: Body,
|
||||
// On disk location of message
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -241,19 +243,7 @@ impl QueryRoot {
|
||||
.headers
|
||||
.get_first_value("date")
|
||||
.and_then(|d| mailparse::dateparse(&d).ok());
|
||||
let body = m.get_body()?;
|
||||
let body = match m.ctype.mimetype.as_str() {
|
||||
"text/plain" => Body::PlainText(PlainText { text: body }),
|
||||
"text/html" => Body::Html(Html { html: body }),
|
||||
_ => {
|
||||
let msg = format!(
|
||||
"Unhandled body content type:\n{}",
|
||||
render_content_type_tree(&m)
|
||||
);
|
||||
warn!("{}", msg);
|
||||
Body::UnhandledContentType(UnhandledContentType { text: msg })
|
||||
}
|
||||
};
|
||||
let body = extract_body(&m)?;
|
||||
messages.push(Message {
|
||||
from,
|
||||
to,
|
||||
@ -261,6 +251,7 @@ impl QueryRoot {
|
||||
subject,
|
||||
timestamp,
|
||||
body,
|
||||
path,
|
||||
});
|
||||
}
|
||||
messages.reverse();
|
||||
@ -276,12 +267,84 @@ 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);
|
||||
}
|
||||
}
|
||||
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 {
|
||||
const WIDTH: usize = 4;
|
||||
fn render_rec(m: &ParsedMail, depth: usize) -> String {
|
||||
let mut parts = Vec::new();
|
||||
let msg = format!("{} {}", "-".repeat(depth * WIDTH), m.ctype.mimetype);
|
||||
println!("{msg}",);
|
||||
parts.push(msg);
|
||||
if !m.ctype.charset.is_empty() {
|
||||
parts.push(format!(
|
||||
|
||||
@ -290,6 +290,22 @@
|
||||
"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,
|
||||
|
||||
@ -28,6 +28,7 @@ query ShowThreadQuery($threadId: String!) {
|
||||
contents
|
||||
}
|
||||
}
|
||||
path
|
||||
}
|
||||
}
|
||||
tags {
|
||||
|
||||
@ -124,12 +124,15 @@ input::placeholder, .input::placeholder{
|
||||
float: right;
|
||||
}
|
||||
/* Hide quoted emails */
|
||||
/*
|
||||
div[name="quote"],
|
||||
blockquote[type="cite"],
|
||||
.gmail_quote {
|
||||
background-color: red;
|
||||
display: none;
|
||||
}
|
||||
*/
|
||||
|
||||
.desktop-main-content {
|
||||
display: grid;
|
||||
grid-template-columns: 12rem 1fr;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user