From 6a15b742b097743176ac2edb2909d3bcf5c0e485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Wed, 12 Oct 2022 15:36:36 +0200 Subject: [PATCH] add sanitize flag for the read command, fix #352 --- CHANGELOG.md | 21 +++++++++++++++++++++ src/domain/email/args.rs | 25 +++++++++++++++++++++---- src/domain/email/handlers.rs | 16 +++++++++++++--- src/main.rs | 3 ++- 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cddf721..eab0e01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +* Added `-s|--sanitize` flag for the `read` command. + +### Changed + +* Changed the behaviour of the `-t|--mime-type` argument of the `read` + command. It is less strict now: if no part is found for the given + MIME type, it will fallback to the other one. For example, giving + `-t html` will show in priority HTML parts, but if none of them are + found it will show plain parts instead (and vice versa). + +* Sanitization is not done by default when using the `read` command, + the flag `-s|--sanitize` needs to be explicitly provided. + +### Fixed + +* Fixed empty text bodies when reading html part on plain text email + [#352]. + ## [0.6.0] - 2022-10-10 ### Changed @@ -571,3 +591,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#340]: https://github.com/soywod/himalaya/issues/340 [#344]: https://github.com/soywod/himalaya/issues/344 [#346]: https://github.com/soywod/himalaya/issues/346 +[#352]: https://github.com/soywod/himalaya/issues/352 diff --git a/src/domain/email/args.rs b/src/domain/email/args.rs index 99dfcba..ec2b643 100644 --- a/src/domain/email/args.rs +++ b/src/domain/email/args.rs @@ -21,6 +21,7 @@ const ARG_PAGE_SIZE: &str = "page-size"; const ARG_QUERY: &str = "query"; const ARG_RAW: &str = "raw"; const ARG_REPLY_ALL: &str = "reply-all"; +const ARG_SANITIZE: &str = "sanitize"; const CMD_ATTACHMENTS: &str = "attachments"; const CMD_COPY: &str = "copy"; const CMD_DEL: &str = "delete"; @@ -41,6 +42,7 @@ type Folder<'a> = &'a str; type Page = usize; type PageSize = usize; type Query = String; +type Sanitize = bool; type Raw = bool; type RawEmail<'a> = &'a str; type TextMime<'a> = &'a str; @@ -60,7 +62,7 @@ pub enum Cmd<'a> { Forward(Id<'a>, Attachments<'a>, Encrypt), List(table::args::MaxTableWidth, Option, Page), Move(Id<'a>, Folder<'a>), - Read(Id<'a>, TextMime<'a>, Raw, Headers<'a>), + Read(Id<'a>, TextMime<'a>, Sanitize, Raw, Headers<'a>), Reply(Id<'a>, All, Attachments<'a>, Encrypt), Save(RawEmail<'a>), Search(Query, table::args::MaxTableWidth, Option, Page), @@ -116,9 +118,10 @@ pub fn matches<'a>(m: &'a ArgMatches) -> Result>> { debug!("read command matched"); let id = parse_id_arg(m); let mime = parse_mime_type_arg(m); + let sanitize = parse_sanitize_flag(m); let raw = parse_raw_flag(m); let headers = parse_headers_arg(m); - Cmd::Read(id, mime, raw, headers) + Cmd::Read(id, mime, sanitize, raw, headers) } else if let Some(m) = m.subcommand_matches(CMD_REPLY) { debug!("reply command matched"); let id = parse_id_arg(m); @@ -210,9 +213,10 @@ pub fn subcmds<'a>() -> Vec> { .about("Saves a raw email") .arg(raw_arg()), SubCommand::with_name(CMD_READ) - .about("Reads text bodies of a email") + .about("Reads text bodies of an email") .arg(id_arg()) .arg(mime_type_arg()) + .arg(sanitize_flag()) .arg(raw_flag()) .arg(headers_arg()), SubCommand::with_name(CMD_REPLY) @@ -399,14 +403,27 @@ pub fn parse_headers_arg<'a>(matches: &'a ArgMatches<'a>) -> Vec<&'a str> { matches.values_of(ARG_HEADERS).unwrap_or_default().collect() } +/// Represents the sanitize flag. +pub fn sanitize_flag<'a>() -> Arg<'a, 'a> { + Arg::with_name(ARG_SANITIZE) + .help("Sanitizes text bodies") + .long("sanitize") + .short("s") +} + /// Represents the raw flag. pub fn raw_flag<'a>() -> Arg<'a, 'a> { Arg::with_name(ARG_RAW) - .help("Reads a raw email") + .help("Returns raw version of email") .long("raw") .short("r") } +/// Represents the sanitize flag parser. +pub fn parse_sanitize_flag<'a>(matches: &'a ArgMatches<'a>) -> bool { + matches.is_present(ARG_SANITIZE) +} + /// Represents the raw flag parser. pub fn parse_raw_flag<'a>(matches: &'a ArgMatches<'a>) -> bool { matches.is_present(ARG_RAW) diff --git a/src/domain/email/handlers.rs b/src/domain/email/handlers.rs index 2e88280..e4be121 100644 --- a/src/domain/email/handlers.rs +++ b/src/domain/email/handlers.rs @@ -5,7 +5,8 @@ use anyhow::{Context, Result}; use atty::Stream; use himalaya_lib::{ - AccountConfig, Backend, Email, Part, Parts, Sender, TextPlainPart, TplOverride, + AccountConfig, Backend, Email, Part, Parts, PartsReaderOptions, Sender, TextPlainPart, + TplOverride, }; use log::{debug, info, trace}; use mailparse::addrparse; @@ -214,6 +215,7 @@ pub fn move_<'a, P: Printer, B: Backend<'a> + ?Sized>( pub fn read<'a, P: Printer, B: Backend<'a> + ?Sized>( seq: &str, text_mime: &str, + sanitize: bool, raw: bool, headers: Vec<&str>, mbox: &str, @@ -224,10 +226,18 @@ pub fn read<'a, P: Printer, B: Backend<'a> + ?Sized>( let msg = backend.email_get(mbox, seq)?; printer.print_struct(if raw { - // Emails don't always have valid utf8. Using "lossy" to display what we can. + // Emails do not always have valid utf8. Using "lossy" to + // display what we can. String::from_utf8_lossy(&msg.raw).into_owned() } else { - msg.to_readable_string(text_mime, headers, config)? + msg.to_readable( + config, + PartsReaderOptions { + plain_first: text_mime == "plain", + sanitize, + }, + headers, + )? }) } diff --git a/src/main.rs b/src/main.rs index 2997150..e9a54bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -161,10 +161,11 @@ fn main() -> Result<()> { Some(email::args::Cmd::Move(seq, mbox_dst)) => { return email::handlers::move_(seq, &folder, mbox_dst, &mut printer, backend.as_mut()); } - Some(email::args::Cmd::Read(seq, text_mime, raw, headers)) => { + Some(email::args::Cmd::Read(seq, text_mime, sanitize, raw, headers)) => { return email::handlers::read( seq, text_mime, + sanitize, raw, headers, &folder,