meli/meli/src/mail/view/types.rs

257 lines
7.3 KiB
Rust

/*
* meli
*
* Copyright 2017 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use std::fmt::Write as IoWrite;
use melib::{
attachment_types::Charset, conf::ActionFlag, error::*, pgp::DecryptionMetadata, Attachment,
Result,
};
use crate::{
conf::shortcuts::EnvelopeViewShortcuts,
jobs::{JobId, JoinHandle},
ShortcutMap, ThemeAttribute, UIDialog,
};
#[derive(Clone, Debug)]
pub struct ViewSettings {
pub pager_filter: Option<String>,
pub html_filter: Option<String>,
pub url_launcher: Option<String>,
pub expand_headers: bool,
pub theme_default: ThemeAttribute,
pub env_view_shortcuts: ShortcutMap,
/// `"mail.view.body"`
pub body_theme: ThemeAttribute,
pub auto_choose_multipart_alternative: bool,
pub sticky_headers: bool,
pub show_date_in_my_timezone: bool,
pub show_extra_headers: Vec<String>,
pub auto_verify_signatures: ActionFlag,
pub auto_decrypt: ActionFlag,
}
impl Default for ViewSettings {
fn default() -> Self {
Self {
theme_default: Default::default(),
body_theme: Default::default(),
pager_filter: None,
html_filter: None,
url_launcher: None,
env_view_shortcuts: EnvelopeViewShortcuts::default().key_values(),
auto_choose_multipart_alternative: true,
expand_headers: false,
sticky_headers: false,
show_date_in_my_timezone: false,
show_extra_headers: vec![],
auto_verify_signatures: ActionFlag::InternalVal(true),
auto_decrypt: ActionFlag::InternalVal(true),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LinkKind {
Url,
Email,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Link {
pub start: usize,
pub end: usize,
pub value: String,
pub kind: LinkKind,
}
#[derive(Debug, Default)]
pub enum ForceCharset {
#[default]
None,
Dialog(Box<UIDialog<Option<Charset>>>),
Forced(Charset),
}
impl From<&ForceCharset> for Option<Charset> {
fn from(val: &ForceCharset) -> Self {
match val {
ForceCharset::Forced(val) => Some(*val),
ForceCharset::None | ForceCharset::Dialog(_) => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Source {
Decoded,
Raw,
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ViewOptions: u8 {
const DEFAULT = 0;
const URL = 1;
const SOURCE = Self::URL.bits() << 1;
const SOURCE_RAW = Self::SOURCE.bits() << 1;
}
}
impl Default for ViewOptions {
fn default() -> Self {
Self::DEFAULT
}
}
impl ViewOptions {
pub fn convert(
&self,
links: &mut Vec<Link>,
attachment: &melib::Attachment,
text: &str,
) -> String {
let mut text = if self.contains(Self::SOURCE) {
if self.contains(Self::SOURCE_RAW) {
String::from_utf8_lossy(attachment.raw()).into_owned()
} else {
/* Decode each header value */
let mut ret = String::new();
match melib::email::parser::headers::headers(attachment.raw()).map(|(_, v)| v) {
Ok(headers) => {
for (h, v) in headers {
_ = match melib::email::parser::encodings::phrase(v, true) {
Ok((_, v)) => ret.write_fmt(format_args!(
"{h}: {}\n",
String::from_utf8_lossy(&v)
)),
Err(err) => ret.write_fmt(format_args!("{h}: {err}\n")),
};
}
}
Err(err) => {
_ = write!(&mut ret, "{err}");
}
}
if !ret.ends_with("\n\n") {
ret.push_str("\n\n");
}
ret.push_str(text);
ret
}
} else {
text.to_string()
};
while text.ends_with("\n\n") {
text.pop();
text.pop();
}
if self.contains(Self::URL) {
if links.is_empty() {
let finder = linkify::LinkFinder::new();
*links = finder
.links(&text)
.filter_map(|l| {
Some(Link {
start: l.start(),
end: l.end(),
value: l.as_str().to_string(),
kind: match l.kind() {
linkify::LinkKind::Url => LinkKind::Url,
linkify::LinkKind::Email => LinkKind::Email,
_ => return None,
},
})
})
.collect::<Vec<Link>>();
}
for (lidx, l) in links.iter().enumerate().rev() {
let mut start = l.start;
while start < text.len() && !text.is_char_boundary(start) {
start += 1;
}
if start < text.len() {
text.insert_str(start, &format!("[{}]", lidx));
}
}
}
text
}
}
#[derive(Debug)]
pub enum AttachmentDisplay {
Alternative {
inner: Box<Attachment>,
shown_display: usize,
display: Vec<AttachmentDisplay>,
},
InlineText {
inner: Box<Attachment>,
comment: Option<String>,
text: String,
},
InlineOther {
inner: Box<Attachment>,
},
Attachment {
inner: Box<Attachment>,
},
SignedPending {
inner: Box<Attachment>,
display: Vec<AttachmentDisplay>,
handle: JoinHandle<Result<()>>,
job_id: JobId,
},
SignedFailed {
inner: Box<Attachment>,
display: Vec<AttachmentDisplay>,
error: Error,
},
SignedUnverified {
inner: Box<Attachment>,
display: Vec<AttachmentDisplay>,
},
SignedVerified {
inner: Box<Attachment>,
display: Vec<AttachmentDisplay>,
description: String,
},
EncryptedPending {
inner: Box<Attachment>,
handle: JoinHandle<Result<(DecryptionMetadata, Vec<u8>)>>,
},
EncryptedFailed {
inner: Box<Attachment>,
error: Error,
},
EncryptedSuccess {
inner: Box<Attachment>,
plaintext: Box<Attachment>,
plaintext_display: Vec<AttachmentDisplay>,
description: String,
},
}