melib/email/attachments: Add DecodeOptions struct for decoding
parent
3688369278
commit
9cbbf71e0f
|
@ -554,7 +554,7 @@ impl Attachment {
|
|||
fn get_text_recursive(&self, text: &mut Vec<u8>) {
|
||||
match self.content_type {
|
||||
ContentType::Text { .. } | ContentType::PGPSignature | ContentType::CMSSignature => {
|
||||
text.extend(decode(self, None));
|
||||
text.extend(self.decode(Default::default()));
|
||||
}
|
||||
ContentType::Multipart {
|
||||
ref kind,
|
||||
|
@ -798,119 +798,129 @@ impl Attachment {
|
|||
})
|
||||
.map(|n| n.replace(|c| std::path::is_separator(c) || c.is_ascii_control(), "_"))
|
||||
}
|
||||
|
||||
fn decode_rec_helper<'a, 'b>(&'a self, options: &mut DecodeOptions<'b>) -> Vec<u8> {
|
||||
match self.content_type {
|
||||
ContentType::Other { .. } => Vec::new(),
|
||||
ContentType::Text { .. } => self.decode_helper(options),
|
||||
ContentType::OctetStream { ref name } => name
|
||||
.clone()
|
||||
.unwrap_or_else(|| self.mime_type())
|
||||
.into_bytes(),
|
||||
ContentType::CMSSignature | ContentType::PGPSignature => Vec::new(),
|
||||
ContentType::MessageRfc822 => {
|
||||
if self.content_disposition.kind.is_inline() {
|
||||
let b = AttachmentBuilder::new(self.body()).build();
|
||||
let ret = b.decode_rec_helper(options);
|
||||
ret
|
||||
} else {
|
||||
b"message/rfc822 attachment".to_vec()
|
||||
}
|
||||
}
|
||||
ContentType::Multipart {
|
||||
ref kind,
|
||||
ref parts,
|
||||
..
|
||||
} => match kind {
|
||||
MultipartType::Alternative => {
|
||||
for a in parts {
|
||||
if let ContentType::Text {
|
||||
kind: Text::Plain, ..
|
||||
} = a.content_type
|
||||
{
|
||||
return a.decode_helper(options);
|
||||
}
|
||||
}
|
||||
self.decode_helper(options)
|
||||
}
|
||||
MultipartType::Signed => {
|
||||
let mut vec = Vec::new();
|
||||
for a in parts {
|
||||
vec.extend(a.decode_rec_helper(options));
|
||||
}
|
||||
vec.extend(self.decode_helper(options));
|
||||
vec
|
||||
}
|
||||
MultipartType::Encrypted => {
|
||||
let mut vec = Vec::new();
|
||||
for a in parts {
|
||||
if a.content_type == "application/octet-stream" {
|
||||
vec.extend(a.decode_rec_helper(options));
|
||||
}
|
||||
}
|
||||
vec.extend(self.decode_helper(options));
|
||||
vec
|
||||
}
|
||||
_ => {
|
||||
let mut vec = Vec::new();
|
||||
for a in parts {
|
||||
if a.content_disposition.kind.is_inline() {
|
||||
vec.extend(a.decode_rec_helper(options));
|
||||
}
|
||||
}
|
||||
vec
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_rec<'a, 'b>(&'a self, mut options: DecodeOptions<'b>) -> Vec<u8> {
|
||||
self.decode_rec_helper(&mut options)
|
||||
}
|
||||
|
||||
fn decode_helper<'a, 'b>(&'a self, options: &mut DecodeOptions<'b>) -> Vec<u8> {
|
||||
let charset = options
|
||||
.force_charset
|
||||
.unwrap_or_else(|| match self.content_type {
|
||||
ContentType::Text { charset, .. } => charset,
|
||||
_ => Default::default(),
|
||||
});
|
||||
|
||||
let bytes = match self.content_transfer_encoding {
|
||||
ContentTransferEncoding::Base64 => match BASE64_MIME.decode(self.body()) {
|
||||
Ok(v) => v,
|
||||
_ => self.body().to_vec(),
|
||||
},
|
||||
ContentTransferEncoding::QuotedPrintable => {
|
||||
parser::encodings::quoted_printable_bytes(self.body())
|
||||
.unwrap()
|
||||
.1
|
||||
}
|
||||
ContentTransferEncoding::_7Bit
|
||||
| ContentTransferEncoding::_8Bit
|
||||
| ContentTransferEncoding::Other { .. } => self.body().to_vec(),
|
||||
};
|
||||
|
||||
let mut ret = if self.content_type.is_text() {
|
||||
if let Ok(v) = parser::encodings::decode_charset(&bytes, charset) {
|
||||
v.into_bytes()
|
||||
} else {
|
||||
self.body().to_vec()
|
||||
}
|
||||
} else {
|
||||
bytes.to_vec()
|
||||
};
|
||||
|
||||
if let Some(filter) = options.filter.as_mut() {
|
||||
filter(self, &mut ret);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn decode<'a, 'b>(&'a self, mut options: DecodeOptions<'b>) -> Vec<u8> {
|
||||
self.decode_helper(&mut options)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interpret_format_flowed(_t: &str) -> String {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
type Filter<'a> = Box<dyn FnMut(&Attachment, &mut Vec<u8>) + 'a>;
|
||||
pub type Filter<'a> = Box<dyn FnMut(&Attachment, &mut Vec<u8>) + 'a>;
|
||||
|
||||
fn decode_rec_helper<'a, 'b>(a: &'a Attachment, filter: &mut Option<Filter<'b>>) -> Vec<u8> {
|
||||
match a.content_type {
|
||||
ContentType::Other { .. } => Vec::new(),
|
||||
ContentType::Text { .. } => decode_helper(a, filter),
|
||||
ContentType::OctetStream { ref name } => {
|
||||
name.clone().unwrap_or_else(|| a.mime_type()).into_bytes()
|
||||
}
|
||||
ContentType::CMSSignature | ContentType::PGPSignature => Vec::new(),
|
||||
ContentType::MessageRfc822 => {
|
||||
if a.content_disposition.kind.is_inline() {
|
||||
let b = AttachmentBuilder::new(a.body()).build();
|
||||
let ret = decode_rec_helper(&b, filter);
|
||||
ret
|
||||
} else {
|
||||
b"message/rfc822 attachment".to_vec()
|
||||
}
|
||||
}
|
||||
ContentType::Multipart {
|
||||
ref kind,
|
||||
ref parts,
|
||||
..
|
||||
} => match kind {
|
||||
MultipartType::Alternative => {
|
||||
for a in parts {
|
||||
if let ContentType::Text {
|
||||
kind: Text::Plain, ..
|
||||
} = a.content_type
|
||||
{
|
||||
return decode_helper(a, filter);
|
||||
}
|
||||
}
|
||||
decode_helper(a, filter)
|
||||
}
|
||||
MultipartType::Signed => {
|
||||
let mut vec = Vec::new();
|
||||
for a in parts {
|
||||
vec.extend(decode_rec_helper(a, filter));
|
||||
}
|
||||
vec.extend(decode_helper(a, filter));
|
||||
vec
|
||||
}
|
||||
MultipartType::Encrypted => {
|
||||
let mut vec = Vec::new();
|
||||
for a in parts {
|
||||
if a.content_type == "application/octet-stream" {
|
||||
vec.extend(decode_rec_helper(a, filter));
|
||||
}
|
||||
}
|
||||
vec.extend(decode_helper(a, filter));
|
||||
vec
|
||||
}
|
||||
_ => {
|
||||
let mut vec = Vec::new();
|
||||
for a in parts {
|
||||
if a.content_disposition.kind.is_inline() {
|
||||
vec.extend(decode_rec_helper(a, filter));
|
||||
}
|
||||
}
|
||||
vec
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_rec<'a, 'b>(a: &'a Attachment, mut filter: Option<Filter<'b>>) -> Vec<u8> {
|
||||
decode_rec_helper(a, &mut filter)
|
||||
}
|
||||
|
||||
fn decode_helper<'a, 'b>(a: &'a Attachment, filter: &mut Option<Filter<'b>>) -> Vec<u8> {
|
||||
let charset = match a.content_type {
|
||||
ContentType::Text { charset: c, .. } => c,
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
let bytes = match a.content_transfer_encoding {
|
||||
ContentTransferEncoding::Base64 => match BASE64_MIME.decode(a.body()) {
|
||||
Ok(v) => v,
|
||||
_ => a.body().to_vec(),
|
||||
},
|
||||
ContentTransferEncoding::QuotedPrintable => {
|
||||
parser::encodings::quoted_printable_bytes(a.body())
|
||||
.unwrap()
|
||||
.1
|
||||
}
|
||||
ContentTransferEncoding::_7Bit
|
||||
| ContentTransferEncoding::_8Bit
|
||||
| ContentTransferEncoding::Other { .. } => a.body().to_vec(),
|
||||
};
|
||||
|
||||
let mut ret = if a.content_type.is_text() {
|
||||
if let Ok(v) = parser::encodings::decode_charset(&bytes, charset) {
|
||||
v.into_bytes()
|
||||
} else {
|
||||
a.body().to_vec()
|
||||
}
|
||||
} else {
|
||||
bytes.to_vec()
|
||||
};
|
||||
if let Some(filter) = filter {
|
||||
filter(a, &mut ret);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn decode<'a, 'b>(a: &'a Attachment, mut filter: Option<Filter<'b>>) -> Vec<u8> {
|
||||
decode_helper(a, &mut filter)
|
||||
#[derive(Default)]
|
||||
pub struct DecodeOptions<'att> {
|
||||
pub filter: Option<Filter<'att>>,
|
||||
pub force_charset: Option<Charset>,
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ use super::*;
|
|||
use crate::email::attachment_types::{
|
||||
Charset, ContentTransferEncoding, ContentType, MultipartType,
|
||||
};
|
||||
use crate::email::attachments::{decode, decode_rec, AttachmentBuilder};
|
||||
use crate::email::attachments::AttachmentBuilder;
|
||||
use crate::shellexpand::ShellExpandTrait;
|
||||
use data_encoding::BASE64_MIME;
|
||||
use std::ffi::OsStr;
|
||||
|
@ -92,7 +92,7 @@ impl FromStr for Draft {
|
|||
}
|
||||
let body = Envelope::new(0).body_bytes(s.as_bytes());
|
||||
|
||||
ret.body = String::from_utf8(decode(&body, None))?;
|
||||
ret.body = String::from_utf8(body.decode(Default::default()))?;
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ impl Draft {
|
|||
);
|
||||
let body = envelope.body_bytes(bytes);
|
||||
ret.body = {
|
||||
let reply_body_bytes = decode_rec(&body, None);
|
||||
let reply_body_bytes = body.decode_rec(Default::default());
|
||||
let reply_body = String::from_utf8_lossy(&reply_body_bytes);
|
||||
let lines: Vec<&str> = reply_body.lines().collect();
|
||||
let mut ret = format!(
|
||||
|
|
|
@ -733,7 +733,7 @@ impl MailView {
|
|||
inner: Box::new(a.clone()),
|
||||
});
|
||||
} else if a.content_type().is_text_html() {
|
||||
let bytes = decode(a, None);
|
||||
let bytes = a.decode(Default::default());
|
||||
let filter_invocation =
|
||||
mailbox_settings!(context[coordinates.0][&coordinates.1].pager.html_filter)
|
||||
.as_ref()
|
||||
|
@ -788,7 +788,7 @@ impl MailView {
|
|||
}
|
||||
}
|
||||
} else if a.is_text() {
|
||||
let bytes = decode(a, None);
|
||||
let bytes = a.decode(Default::default());
|
||||
acc.push(AttachmentDisplay::InlineText {
|
||||
inner: Box::new(a.clone()),
|
||||
comment: None,
|
||||
|
@ -810,7 +810,7 @@ impl MailView {
|
|||
if let Some(text_attachment_pos) =
|
||||
parts.iter().position(|a| a.content_type == "text/plain")
|
||||
{
|
||||
let bytes = decode(&parts[text_attachment_pos], None);
|
||||
let bytes = &parts[text_attachment_pos].decode(Default::default());
|
||||
if bytes.trim().is_empty()
|
||||
&& mailbox_settings!(
|
||||
context[coordinates.0][&coordinates.1]
|
||||
|
@ -2211,7 +2211,7 @@ impl Component for MailView {
|
|||
let filename = attachment.filename();
|
||||
if let Ok(command) = query_default_app(&attachment_type) {
|
||||
let p = create_temp_file(
|
||||
&decode(attachment, None),
|
||||
&attachment.decode(Default::default()),
|
||||
filename.as_deref(),
|
||||
None,
|
||||
true,
|
||||
|
@ -2466,7 +2466,7 @@ impl Component for MailView {
|
|||
path.push(u.as_hyphenated().to_string());
|
||||
}
|
||||
}
|
||||
match save_attachment(&path, &decode(u, None)) {
|
||||
match save_attachment(&path, &u.decode(Default::default())) {
|
||||
Err(err) => {
|
||||
context.replies.push_back(UIEvent::Notification(
|
||||
Some(format!("Failed to create file at {}", path.display())),
|
||||
|
|
|
@ -83,9 +83,8 @@ impl EnvelopeView {
|
|||
/// Returns the string to be displayed in the Viewer
|
||||
fn attachment_to_text(&self, body: &Attachment, context: &mut Context) -> String {
|
||||
let finder = LinkFinder::new();
|
||||
let body_text = String::from_utf8_lossy(&decode_rec(
|
||||
body,
|
||||
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
|
||||
let body_text = String::from_utf8_lossy(&body.decode_rec(DecodeOptions {
|
||||
filter: Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
|
||||
if a.content_type().is_text_html() {
|
||||
let settings = &context.settings;
|
||||
if let Some(filter_invocation) = settings.pager.html_filter.as_ref() {
|
||||
|
@ -123,7 +122,8 @@ impl EnvelopeView {
|
|||
}
|
||||
}
|
||||
})),
|
||||
))
|
||||
..Default::default()
|
||||
}))
|
||||
.into_owned();
|
||||
match self.mode {
|
||||
ViewMode::Normal | ViewMode::Subview => {
|
||||
|
@ -370,7 +370,8 @@ impl Component for EnvelopeView {
|
|||
self.mode = ViewMode::Subview;
|
||||
let colors = crate::conf::value(context, "mail.view.body");
|
||||
self.subview = Some(Box::new(Pager::from_string(
|
||||
String::from_utf8_lossy(&decode_rec(u, None)).to_string(),
|
||||
String::from_utf8_lossy(&u.decode_rec(Default::default()))
|
||||
.to_string(),
|
||||
Some(context),
|
||||
None,
|
||||
None,
|
||||
|
@ -397,7 +398,7 @@ impl Component for EnvelopeView {
|
|||
let filename = u.filename();
|
||||
if let Ok(command) = query_default_app(&attachment_type) {
|
||||
let p = create_temp_file(
|
||||
&decode(u, None),
|
||||
&u.decode(Default::default()),
|
||||
filename.as_deref(),
|
||||
None,
|
||||
true,
|
||||
|
|
|
@ -33,7 +33,7 @@ pub struct HtmlView {
|
|||
impl HtmlView {
|
||||
pub fn new(body: &Attachment, context: &mut Context) -> Self {
|
||||
let id = ComponentId::new_v4();
|
||||
let bytes: Vec<u8> = decode_rec(body, None);
|
||||
let bytes: Vec<u8> = body.decode_rec(Default::default());
|
||||
|
||||
let settings = &context.settings;
|
||||
let mut display_text = if let Some(filter_invocation) = settings.pager.html_filter.as_ref()
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
*/
|
||||
use crate::state::Context;
|
||||
use crate::types::{create_temp_file, ForkType, UIEvent};
|
||||
use melib::attachments::decode;
|
||||
use melib::text_processing::GlobMatch;
|
||||
use melib::{email::Attachment, MeliError, Result};
|
||||
use std::collections::HashMap;
|
||||
|
@ -159,7 +158,8 @@ impl MailcapEntry {
|
|||
.map(|arg| match *arg {
|
||||
"%s" => {
|
||||
needs_stdin = false;
|
||||
let _f = create_temp_file(&decode(a, None), None, None, true);
|
||||
let _f =
|
||||
create_temp_file(&a.decode(Default::default()), None, None, true);
|
||||
let p = _f.path().display().to_string();
|
||||
f = Some(_f);
|
||||
p
|
||||
|
@ -191,7 +191,11 @@ impl MailcapEntry {
|
|||
.stdout(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
child.stdin.as_mut().unwrap().write_all(&decode(a, None))?;
|
||||
child
|
||||
.stdin
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.write_all(&a.decode(Default::default()))?;
|
||||
child.wait_with_output()?.stdout
|
||||
} else {
|
||||
let child = Command::new("sh")
|
||||
|
@ -221,7 +225,11 @@ impl MailcapEntry {
|
|||
.stdout(Stdio::inherit())
|
||||
.spawn()?;
|
||||
|
||||
child.stdin.as_mut().unwrap().write_all(&decode(a, None))?;
|
||||
child
|
||||
.stdin
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.write_all(&a.decode(Default::default()))?;
|
||||
debug!(child.wait_with_output()?.stdout);
|
||||
} else {
|
||||
let child = Command::new("sh")
|
||||
|
|
|
@ -34,7 +34,7 @@ use termion::color::{AnsiValue, Rgb as TermionRgb};
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// ```no_run
|
||||
/// use meli::Color;
|
||||
///
|
||||
/// // The default color.
|
||||
|
|
Loading…
Reference in New Issue