mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-16 20:57:53 +08:00
230 lines
5.5 KiB
Rust
230 lines
5.5 KiB
Rust
use imap;
|
|
use mailparse::{self, MailHeaderMap};
|
|
use rfc2047_decoder;
|
|
|
|
use crate::table::{self, DisplayCell, DisplayRow, DisplayTable};
|
|
|
|
#[derive(Debug)]
|
|
pub struct Uid(pub u32);
|
|
|
|
impl Uid {
|
|
pub fn from_fetch(fetch: &imap::types::Fetch) -> Self {
|
|
Self(fetch.uid.unwrap())
|
|
}
|
|
}
|
|
|
|
impl DisplayCell for Uid {
|
|
fn styles(&self) -> &[table::Style] {
|
|
&[table::RED]
|
|
}
|
|
|
|
fn value(&self) -> String {
|
|
self.0.to_string()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Flags<'a>(Vec<imap::types::Flag<'a>>);
|
|
|
|
impl Flags<'_> {
|
|
pub fn from_fetch(fetch: &imap::types::Fetch) -> Self {
|
|
let flags = fetch.flags().iter().fold(vec![], |mut flags, flag| {
|
|
use imap::types::Flag::*;
|
|
|
|
match flag {
|
|
Seen => flags.push(Seen),
|
|
Answered => flags.push(Answered),
|
|
Draft => flags.push(Draft),
|
|
Flagged => flags.push(Flagged),
|
|
_ => (),
|
|
};
|
|
|
|
flags
|
|
});
|
|
|
|
Self(flags)
|
|
}
|
|
}
|
|
|
|
impl DisplayCell for Flags<'_> {
|
|
fn styles(&self) -> &[table::Style] {
|
|
&[table::WHITE]
|
|
}
|
|
|
|
fn value(&self) -> String {
|
|
// FIXME
|
|
// use imap::types::Flag::*;
|
|
|
|
// let flags = &self.0;
|
|
// let mut flags_str = String::new();
|
|
|
|
// flags_str.push_str(if flags.contains(&Seen) { &" " } else { &"N" });
|
|
// flags_str.push_str(if flags.contains(&Answered) {
|
|
// &"R"
|
|
// } else {
|
|
// &" "
|
|
// });
|
|
// flags_str.push_str(if flags.contains(&Draft) { &"D" } else { &" " });
|
|
// flags_str.push_str(if flags.contains(&Flagged) { &"F" } else { &" " });
|
|
|
|
// flags_str
|
|
|
|
String::new()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Sender(String);
|
|
|
|
impl Sender {
|
|
fn try_from_fetch(fetch: &imap::types::Fetch) -> Option<String> {
|
|
let addr = fetch.envelope()?.from.as_ref()?.first()?;
|
|
|
|
addr.name
|
|
.and_then(|bytes| rfc2047_decoder::decode(bytes).ok())
|
|
.or_else(|| {
|
|
let mbox = String::from_utf8(addr.mailbox?.to_vec()).ok()?;
|
|
let host = String::from_utf8(addr.host?.to_vec()).ok()?;
|
|
Some(format!("{}@{}", mbox, host))
|
|
})
|
|
}
|
|
|
|
pub fn from_fetch(fetch: &imap::types::Fetch) -> Self {
|
|
Self(Self::try_from_fetch(fetch).unwrap_or(String::new()))
|
|
}
|
|
}
|
|
|
|
impl DisplayCell for Sender {
|
|
fn styles(&self) -> &[table::Style] {
|
|
&[table::BLUE]
|
|
}
|
|
|
|
fn value(&self) -> String {
|
|
self.0.to_owned()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Subject(String);
|
|
|
|
impl Subject {
|
|
fn try_from_fetch(fetch: &imap::types::Fetch) -> Option<String> {
|
|
fetch
|
|
.envelope()?
|
|
.subject
|
|
.and_then(|bytes| rfc2047_decoder::decode(bytes).ok())
|
|
.and_then(|subject| Some(subject.replace("\r", "")))
|
|
.and_then(|subject| Some(subject.replace("\n", "")))
|
|
}
|
|
|
|
pub fn from_fetch(fetch: &imap::types::Fetch) -> Self {
|
|
Self(Self::try_from_fetch(fetch).unwrap_or(String::new()))
|
|
}
|
|
}
|
|
|
|
impl DisplayCell for Subject {
|
|
fn styles(&self) -> &[table::Style] {
|
|
&[table::GREEN]
|
|
}
|
|
|
|
fn value(&self) -> String {
|
|
self.0.to_owned()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Date(String);
|
|
|
|
impl Date {
|
|
fn try_from_fetch(fetch: &imap::types::Fetch) -> Option<String> {
|
|
fetch
|
|
.internal_date()
|
|
.and_then(|date| Some(date.to_rfc3339()))
|
|
}
|
|
|
|
pub fn from_fetch(fetch: &imap::types::Fetch) -> Self {
|
|
Self(Self::try_from_fetch(fetch).unwrap_or(String::new()))
|
|
}
|
|
}
|
|
|
|
impl DisplayCell for Date {
|
|
fn styles(&self) -> &[table::Style] {
|
|
&[table::YELLOW]
|
|
}
|
|
|
|
fn value(&self) -> String {
|
|
self.0.to_owned()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Email<'a> {
|
|
pub uid: Uid,
|
|
pub flags: Flags<'a>,
|
|
pub from: Sender,
|
|
pub subject: Subject,
|
|
pub date: Date,
|
|
}
|
|
|
|
impl Email<'_> {
|
|
pub fn from_fetch(fetch: &imap::types::Fetch) -> Self {
|
|
Self {
|
|
uid: Uid::from_fetch(fetch),
|
|
from: Sender::from_fetch(fetch),
|
|
subject: Subject::from_fetch(fetch),
|
|
date: Date::from_fetch(fetch),
|
|
flags: Flags::from_fetch(fetch),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> DisplayRow for Email<'a> {
|
|
fn to_row(&self) -> Vec<table::Cell> {
|
|
vec![
|
|
self.uid.to_cell(),
|
|
self.flags.to_cell(),
|
|
self.from.to_cell(),
|
|
self.subject.to_cell(),
|
|
self.date.to_cell(),
|
|
]
|
|
}
|
|
}
|
|
|
|
impl<'a> DisplayTable<'a, Email<'a>> for Vec<Email<'a>> {
|
|
fn cols() -> &'a [&'a str] {
|
|
&["uid", "flags", "from", "subject", "date"]
|
|
}
|
|
|
|
fn rows(&self) -> &Vec<Email<'a>> {
|
|
self
|
|
}
|
|
}
|
|
|
|
// Utils
|
|
|
|
fn extract_text_bodies_into(mime: &str, part: &mailparse::ParsedMail, parts: &mut Vec<String>) {
|
|
match part.subparts.len() {
|
|
0 => {
|
|
if part
|
|
.get_headers()
|
|
.get_first_value("content-type")
|
|
.and_then(|v| if v.starts_with(&mime) { Some(()) } else { None })
|
|
.is_some()
|
|
{
|
|
parts.push(part.get_body().unwrap_or(String::new()))
|
|
}
|
|
}
|
|
_ => {
|
|
part.subparts
|
|
.iter()
|
|
.for_each(|part| extract_text_bodies_into(&mime, part, parts));
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn extract_text_bodies(mime: &str, email: &mailparse::ParsedMail) -> String {
|
|
let mut parts = vec![];
|
|
extract_text_bodies_into(&mime, email, &mut parts);
|
|
parts.join("\r\n")
|
|
}
|