web & server: add handling for google calendar and wellsfargo emails.

This commit is contained in:
Bill Thiede 2023-11-26 20:51:53 -08:00
parent 1261bdf8a9
commit c8147ded60
4 changed files with 98 additions and 15 deletions

View File

@ -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!(

View File

@ -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,

View File

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

View File

@ -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;