mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-08 18:45:13 +00:00
introduce read_headers in account config (#338)
This commit is contained in:
parent
d3968461e2
commit
f9bed5f3c2
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- SMTP pre-send hook [#178]
|
- SMTP pre-send hook [#178]
|
||||||
|
- Customize headers to show at the top of a read message [#338]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -503,3 +504,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
[#329]: https://github.com/soywod/himalaya/issues/329
|
[#329]: https://github.com/soywod/himalaya/issues/329
|
||||||
[#331]: https://github.com/soywod/himalaya/issues/331
|
[#331]: https://github.com/soywod/himalaya/issues/331
|
||||||
[#335]: https://github.com/soywod/himalaya/issues/335
|
[#335]: https://github.com/soywod/himalaya/issues/335
|
||||||
|
[#338]: https://github.com/soywod/himalaya/issues/338
|
||||||
|
|
|
@ -32,6 +32,9 @@ pub struct AccountConfig {
|
||||||
/// Represents the text/plain format as defined in the
|
/// Represents the text/plain format as defined in the
|
||||||
/// [RFC2646](https://www.ietf.org/rfc/rfc2646.txt)
|
/// [RFC2646](https://www.ietf.org/rfc/rfc2646.txt)
|
||||||
pub format: Format,
|
pub format: Format,
|
||||||
|
/// Overrides the default headers displayed at the top of
|
||||||
|
/// the read message.
|
||||||
|
pub read_headers: Vec<String>,
|
||||||
|
|
||||||
/// Represents mailbox aliases.
|
/// Represents mailbox aliases.
|
||||||
pub mailboxes: HashMap<String, String>,
|
pub mailboxes: HashMap<String, String>,
|
||||||
|
@ -157,6 +160,7 @@ impl<'a> AccountConfig {
|
||||||
.unwrap_or(&vec![])
|
.unwrap_or(&vec![])
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
format: base_account.format.unwrap_or_default(),
|
format: base_account.format.unwrap_or_default(),
|
||||||
|
read_headers: base_account.read_headers,
|
||||||
mailboxes: base_account.mailboxes.clone(),
|
mailboxes: base_account.mailboxes.clone(),
|
||||||
hooks: base_account.hooks.unwrap_or_default(),
|
hooks: base_account.hooks.unwrap_or_default(),
|
||||||
default: base_account.default.unwrap_or_default(),
|
default: base_account.default.unwrap_or_default(),
|
||||||
|
|
|
@ -45,7 +45,7 @@ macro_rules! make_account_config {
|
||||||
pub signature: Option<String>,
|
pub signature: Option<String>,
|
||||||
/// Overrides the signature delimiter for this account.
|
/// Overrides the signature delimiter for this account.
|
||||||
pub signature_delimiter: Option<String>,
|
pub signature_delimiter: Option<String>,
|
||||||
/// Overrides the default page size for this account.
|
/// Overrides the default page size for this account.
|
||||||
pub default_page_size: Option<usize>,
|
pub default_page_size: Option<usize>,
|
||||||
/// Overrides the notify command for this account.
|
/// Overrides the notify command for this account.
|
||||||
pub notify_cmd: Option<String>,
|
pub notify_cmd: Option<String>,
|
||||||
|
@ -56,6 +56,10 @@ macro_rules! make_account_config {
|
||||||
/// Represents the text/plain format as defined in the
|
/// Represents the text/plain format as defined in the
|
||||||
/// [RFC2646](https://www.ietf.org/rfc/rfc2646.txt)
|
/// [RFC2646](https://www.ietf.org/rfc/rfc2646.txt)
|
||||||
pub format: Option<Format>,
|
pub format: Option<Format>,
|
||||||
|
/// Overrides the default headers displayed at the top of
|
||||||
|
/// the read message.
|
||||||
|
#[serde(default)]
|
||||||
|
pub read_headers: Vec<String>,
|
||||||
|
|
||||||
/// Makes this account the default one.
|
/// Makes this account the default one.
|
||||||
pub default: Option<bool>,
|
pub default: Option<bool>,
|
||||||
|
@ -102,6 +106,7 @@ macro_rules! make_account_config {
|
||||||
notify_query: self.notify_query.clone(),
|
notify_query: self.notify_query.clone(),
|
||||||
watch_cmds: self.watch_cmds.clone(),
|
watch_cmds: self.watch_cmds.clone(),
|
||||||
format: self.format.clone(),
|
format: self.format.clone(),
|
||||||
|
read_headers: self.read_headers.clone(),
|
||||||
|
|
||||||
default: self.default.clone(),
|
default: self.default.clone(),
|
||||||
email: self.email.clone(),
|
email: self.email.clone(),
|
||||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -222,7 +222,16 @@ fn main() -> Result<()> {
|
||||||
return msg_handlers::move_(seq, mbox, mbox_dst, &mut printer, backend);
|
return msg_handlers::move_(seq, mbox, mbox_dst, &mut printer, backend);
|
||||||
}
|
}
|
||||||
Some(msg_args::Cmd::Read(seq, text_mime, raw, headers)) => {
|
Some(msg_args::Cmd::Read(seq, text_mime, raw, headers)) => {
|
||||||
return msg_handlers::read(seq, text_mime, raw, headers, mbox, &mut printer, backend);
|
return msg_handlers::read(
|
||||||
|
seq,
|
||||||
|
text_mime,
|
||||||
|
raw,
|
||||||
|
headers,
|
||||||
|
mbox,
|
||||||
|
&account_config,
|
||||||
|
&mut printer,
|
||||||
|
backend,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Some(msg_args::Cmd::Reply(seq, all, attachment_paths, encrypt)) => {
|
Some(msg_args::Cmd::Reply(seq, all, attachment_paths, encrypt)) => {
|
||||||
return msg_handlers::reply(
|
return msg_handlers::reply(
|
||||||
|
|
|
@ -729,11 +729,29 @@ impl Msg {
|
||||||
/// message is like a template, except that:
|
/// message is like a template, except that:
|
||||||
/// - headers part is customizable (can be omitted if empty filter given in argument)
|
/// - headers part is customizable (can be omitted if empty filter given in argument)
|
||||||
/// - body type is customizable (plain or html)
|
/// - body type is customizable (plain or html)
|
||||||
pub fn to_readable_string(&self, text_mime: &str, headers: Vec<&str>) -> Result<String> {
|
pub fn to_readable_string(
|
||||||
let mut readable_msg = String::new();
|
&self,
|
||||||
|
text_mime: &str,
|
||||||
|
headers: Vec<&str>,
|
||||||
|
config: &AccountConfig,
|
||||||
|
) -> Result<String> {
|
||||||
|
let mut all_headers = vec![];
|
||||||
|
for h in config.read_headers.iter() {
|
||||||
|
let h = h.to_lowercase();
|
||||||
|
if !all_headers.contains(&h) {
|
||||||
|
all_headers.push(h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for h in headers.iter() {
|
||||||
|
let h = h.to_lowercase();
|
||||||
|
if !all_headers.contains(&h) {
|
||||||
|
all_headers.push(h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for h in headers {
|
let mut readable_msg = String::new();
|
||||||
match h.to_lowercase().as_str() {
|
for h in all_headers {
|
||||||
|
match h.as_str() {
|
||||||
"message-id" => match self.message_id {
|
"message-id" => match self.message_id {
|
||||||
Some(ref message_id) if !message_id.is_empty() => {
|
Some(ref message_id) if !message_id.is_empty() => {
|
||||||
readable_msg.push_str(&format!("Message-Id: {}\n", message_id));
|
readable_msg.push_str(&format!("Message-Id: {}\n", message_id));
|
||||||
|
@ -833,9 +851,8 @@ impl TryInto<lettre::address::Envelope> for &Msg {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::iter::FromIterator;
|
|
||||||
|
|
||||||
use mailparse::SingleInfo;
|
use mailparse::SingleInfo;
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
use crate::msg::Addr;
|
use crate::msg::Addr;
|
||||||
|
|
||||||
|
@ -982,6 +999,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_to_readable() {
|
fn test_to_readable() {
|
||||||
|
let config = AccountConfig::default();
|
||||||
let msg = Msg {
|
let msg = Msg {
|
||||||
parts: Parts(vec![Part::TextPlain(TextPlainPart {
|
parts: Parts(vec![Part::TextPlain(TextPlainPart {
|
||||||
content: String::from("hello, world!"),
|
content: String::from("hello, world!"),
|
||||||
|
@ -989,21 +1007,22 @@ mod tests {
|
||||||
..Msg::default()
|
..Msg::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// empty msg, empty headers
|
// empty msg headers, empty headers, empty config
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"hello, world!",
|
"hello, world!",
|
||||||
msg.to_readable_string("plain", vec![]).unwrap()
|
msg.to_readable_string("plain", vec![], &config).unwrap()
|
||||||
);
|
);
|
||||||
// empty msg, basic headers
|
// empty msg headers, basic headers
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"hello, world!",
|
"hello, world!",
|
||||||
msg.to_readable_string("plain", vec!["from", "date", "custom-header"])
|
msg.to_readable_string("plain", vec!["From", "DATE", "custom-hEader"], &config)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
// empty msg, subject header
|
// empty msg headers, multiple subject headers
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"Subject: \n\nhello, world!",
|
"Subject: \n\nhello, world!",
|
||||||
msg.to_readable_string("plain", vec!["subject"]).unwrap()
|
msg.to_readable_string("plain", vec!["subject", "Subject", "SUBJECT"], &config)
|
||||||
|
.unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
let msg = Msg {
|
let msg = Msg {
|
||||||
|
@ -1023,26 +1042,39 @@ mod tests {
|
||||||
..Msg::default()
|
..Msg::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// header present in msg headers
|
// header present in msg headers, empty config
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"From: \"Test\" <test@local>\n\nhello, world!",
|
"From: \"Test\" <test@local>\n\nhello, world!",
|
||||||
msg.to_readable_string("plain", vec!["from"]).unwrap()
|
msg.to_readable_string("plain", vec!["from"], &config)
|
||||||
);
|
|
||||||
// header present but empty in msg headers
|
|
||||||
assert_eq!(
|
|
||||||
"hello, world!",
|
|
||||||
msg.to_readable_string("plain", vec!["cc"]).unwrap()
|
|
||||||
);
|
|
||||||
// custom header present in msg headers
|
|
||||||
assert_eq!(
|
|
||||||
"Custom-Header: custom value\n\nhello, world!",
|
|
||||||
msg.to_readable_string("plain", vec!["custom-header"])
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
// custom header present in msg headers (case insensitivity)
|
// header present but empty in msg headers, empty config
|
||||||
|
assert_eq!(
|
||||||
|
"hello, world!",
|
||||||
|
msg.to_readable_string("plain", vec!["cc"], &config)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
// multiple same custom headers present in msg headers, empty
|
||||||
|
// config
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"Custom-Header: custom value\n\nhello, world!",
|
"Custom-Header: custom value\n\nhello, world!",
|
||||||
msg.to_readable_string("plain", vec!["CUSTOM-hEaDer"])
|
msg.to_readable_string("plain", vec!["custom-header", "cuSTom-HeaDer"], &config)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
let config = AccountConfig {
|
||||||
|
read_headers: vec![
|
||||||
|
"CusTOM-heaDER".into(),
|
||||||
|
"Subject".into(),
|
||||||
|
"from".into(),
|
||||||
|
"cc".into(),
|
||||||
|
],
|
||||||
|
..AccountConfig::default()
|
||||||
|
};
|
||||||
|
// header present but empty in msg headers, empty config
|
||||||
|
assert_eq!(
|
||||||
|
"Custom-Header: custom value\nSubject: \nFrom: \"Test\" <test@local>\nMessage-Id: <message-id>\n\nhello, world!",
|
||||||
|
msg.to_readable_string("plain", vec!["cc", "message-ID"], &config)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,6 +209,7 @@ pub fn read<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
raw: bool,
|
raw: bool,
|
||||||
headers: Vec<&str>,
|
headers: Vec<&str>,
|
||||||
mbox: &str,
|
mbox: &str,
|
||||||
|
config: &AccountConfig,
|
||||||
printer: &mut P,
|
printer: &mut P,
|
||||||
backend: Box<&'a mut B>,
|
backend: Box<&'a mut B>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
@ -218,7 +219,7 @@ pub fn read<'a, P: PrinterService, B: Backend<'a> + ?Sized>(
|
||||||
// Emails don't always have valid utf8. Using "lossy" to display what we can.
|
// Emails don't always have valid utf8. Using "lossy" to display what we can.
|
||||||
String::from_utf8_lossy(&msg.raw).into_owned()
|
String::from_utf8_lossy(&msg.raw).into_owned()
|
||||||
} else {
|
} else {
|
||||||
msg.to_readable_string(text_mime, headers)?
|
msg.to_readable_string(text_mime, headers, config)?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue