diff --git a/melib/src/email.rs b/melib/src/email.rs index f441cb32a..4c4dcd057 100644 --- a/melib/src/email.rs +++ b/melib/src/email.rs @@ -274,16 +274,25 @@ impl Envelope { } } else if name.eq_ignore_ascii_case(b"content-type") { match parser::content_type(value).to_full_result() { - Ok((ct, cst, _)) + Ok((ct, cst, ref params)) if ct.eq_ignore_ascii_case(b"multipart") && cst.eq_ignore_ascii_case(b"mixed") => { - let mut builder = AttachmentBuilder::new(body); + let mut builder = AttachmentBuilder::default(); builder.set_content_type_from_bytes(value); - let b = builder.build(); - let subs = b.attachments(); - - self.has_attachments = subs.iter().any(|sub| !sub.is_text()); + let mut boundary = None; + for (n, v) in params { + if n == b"boundary" { + boundary = Some(v); + break; + } + } + if let Some(boundary) = boundary { + self.has_attachments = + Attachment::check_if_has_attachments_quick(body, boundary); + } else { + debug!("{:?} has no boundary field set in multipart/mixed content-type field.", &self); + } } _ => {} } @@ -373,31 +382,7 @@ impl Envelope { .unwrap_or_else(|_| Vec::new()) } pub fn body_bytes(&self, bytes: &[u8]) -> Attachment { - if bytes.is_empty() { - let builder = AttachmentBuilder::new(bytes); - return builder.build(); - } - - let (headers, body) = match parser::mail(bytes).to_full_result() { - Ok(v) => v, - Err(_) => { - debug!("error in parsing mail\n"); - let error_msg = b"Mail cannot be shown because of errors."; - let builder = AttachmentBuilder::new(error_msg); - return builder.build(); - } - }; - let mut builder = AttachmentBuilder::new(body); - for (name, value) in headers { - if value.len() == 1 && value.is_empty() { - continue; - } - if name.eq_ignore_ascii_case(b"content-transfer-encoding") { - builder.set_content_transfer_encoding(ContentTransferEncoding::from(value)); - } else if name.eq_ignore_ascii_case(b"content-type") { - builder.set_content_type_from_bytes(value); - } - } + let builder = AttachmentBuilder::new(bytes); builder.build() } pub fn headers<'a>(&self, bytes: &'a [u8]) -> Result> { diff --git a/melib/src/email/address.rs b/melib/src/email/address.rs index dd771d928..4845d3281 100644 --- a/melib/src/email/address.rs +++ b/melib/src/email/address.rs @@ -129,7 +129,7 @@ impl fmt::Debug for Address { } /// Helper struct to return slices from a struct field on demand. -#[derive(Clone, Debug, Serialize, Deserialize, Default)] +#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq, Copy)] pub struct StrBuilder { pub offset: usize, pub length: usize, @@ -146,12 +146,13 @@ pub trait StrBuild { } impl StrBuilder { - fn display<'a>(&self, s: &'a [u8]) -> String { + pub fn display<'a>(&self, s: &'a [u8]) -> String { let offset = self.offset; let length = self.length; String::from_utf8(s[offset..offset + length].to_vec()).unwrap() } - fn display_bytes<'a>(&self, b: &'a [u8]) -> &'a [u8] { + + pub fn display_bytes<'a>(&self, b: &'a [u8]) -> &'a [u8] { &b[self.offset..(self.offset + self.length)] } } diff --git a/melib/src/email/attachment_types.rs b/melib/src/email/attachment_types.rs index 307474d57..f5287df61 100644 --- a/melib/src/email/attachment_types.rs +++ b/melib/src/email/attachment_types.rs @@ -20,6 +20,7 @@ */ use crate::email::attachments::{Attachment, AttachmentBuilder}; use crate::email::parser::BytesExt; + use std::fmt::{Display, Formatter, Result as FmtResult}; use std::str; @@ -123,7 +124,7 @@ pub enum ContentType { Multipart { boundary: Vec, kind: MultipartType, - subattachments: Vec, + parts: Vec, }, MessageRfc822, PGPSignature, @@ -178,7 +179,7 @@ impl ContentType { } } - pub fn make_boundary(subattachments: &Vec) -> String { + pub fn make_boundary(parts: &Vec) -> String { use crate::email::compose::random::gen_boundary; let mut boundary = "bzz_bzz__bzz__".to_string(); let mut random_boundary = gen_boundary(); @@ -186,7 +187,7 @@ impl ContentType { let mut loop_counter = 4096; 'loo: loop { let mut flag = true; - for sub in subattachments { + for sub in parts { 'sub_loop: loop { if sub.raw().find(random_boundary.as_bytes()).is_some() { random_boundary = gen_boundary(); diff --git a/melib/src/email/attachments.rs b/melib/src/email/attachments.rs index bea56b181..06cbf4f4a 100644 --- a/melib/src/email/attachments.rs +++ b/melib/src/email/attachments.rs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with meli. If not, see . */ +use crate::email::address::StrBuilder; use crate::email::parser; use crate::email::parser::BytesExt; use crate::email::EnvelopeWrapper; @@ -33,21 +34,59 @@ pub struct AttachmentBuilder { pub content_transfer_encoding: ContentTransferEncoding, pub raw: Vec, + pub body: StrBuilder, } impl AttachmentBuilder { pub fn new(content: &[u8]) -> Self { - AttachmentBuilder { - content_type: Default::default(), - content_transfer_encoding: ContentTransferEncoding::_7Bit, - raw: content.to_vec(), + let (headers, body) = match parser::attachment(content).to_full_result() { + Ok(v) => v, + Err(_) => { + debug!("error in parsing attachment"); + debug!("\n-------------------------------"); + debug!("{}\n", ::std::string::String::from_utf8_lossy(content)); + debug!("-------------------------------\n"); + + return AttachmentBuilder { + content_type: Default::default(), + content_transfer_encoding: ContentTransferEncoding::_7Bit, + raw: content.to_vec(), + body: StrBuilder { + length: content.len(), + offset: 0, + }, + }; + } + }; + + let raw = content.into(); + let body = StrBuilder { + offset: content.len() - body.len(), + length: body.len(), + }; + let mut builder = AttachmentBuilder { + raw, + body, + ..Default::default() + }; + for (name, value) in headers { + if name.eq_ignore_ascii_case(b"content-type") { + builder.set_content_type_from_bytes(value); + } else if name.eq_ignore_ascii_case(b"content-transfer-encoding") { + builder.set_content_transfer_encoding(ContentTransferEncoding::from(value)); + } } + builder } pub fn raw(&self) -> &[u8] { &self.raw } + pub fn body(&self) -> &[u8] { + self.body.display_bytes(&self.raw) + } + pub fn set_raw(&mut self, raw: Vec) -> &mut Self { self.raw = raw; self @@ -84,12 +123,12 @@ impl AttachmentBuilder { } assert!(boundary.is_some()); let boundary = boundary.unwrap().to_vec(); - let subattachments = Self::subattachments(&self.raw, &boundary); + let parts = Self::parts(self.body(), &boundary); self.content_type = ContentType::Multipart { boundary, kind: MultipartType::from(cst), - subattachments, + parts, }; } else if ct.eq_ignore_ascii_case(b"text") { self.content_type = ContentType::Text { @@ -160,15 +199,16 @@ impl AttachmentBuilder { content_type: self.content_type, content_transfer_encoding: self.content_transfer_encoding, raw: self.raw, + body: self.body, } } - pub fn subattachments(raw: &[u8], boundary: &[u8]) -> Vec { + pub fn parts(raw: &[u8], boundary: &[u8]) -> Vec { if raw.is_empty() { return Vec::new(); } - match parser::attachments(raw, boundary).to_full_result() { + match parser::parts(raw, boundary).to_full_result() { Ok(attachments) => { let mut vec = Vec::with_capacity(attachments.len()); for a in attachments { @@ -185,7 +225,11 @@ impl AttachmentBuilder { } }; - builder.raw = body.ltrim().into(); + builder.raw = a.into(); + builder.body = StrBuilder { + offset: a.len() - body.len(), + length: body.len(), + }; for (name, value) in headers { if name.eq_ignore_ascii_case(b"content-type") { builder.set_content_type_from_bytes(value); @@ -218,11 +262,13 @@ impl From for AttachmentBuilder { content_type, content_transfer_encoding, raw, + body, } = val; AttachmentBuilder { content_type, content_transfer_encoding, raw, + body, } } } @@ -230,10 +276,11 @@ impl From for AttachmentBuilder { /// Immutable attachment type. #[derive(Clone, Serialize, Deserialize, PartialEq)] pub struct Attachment { - pub(in crate::email) content_type: ContentType, - pub(in crate::email) content_transfer_encoding: ContentTransferEncoding, + pub content_type: ContentType, + pub content_transfer_encoding: ContentTransferEncoding, - pub(in crate::email) raw: Vec, + pub raw: Vec, + pub body: StrBuilder, } impl fmt::Debug for Attachment { @@ -254,16 +301,18 @@ impl fmt::Debug for Attachment { impl fmt::Display for Attachment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.content_type { - ContentType::MessageRfc822 => match EnvelopeWrapper::new(self.raw().to_vec()) { - Ok(wrapper) => write!( - f, - "message/rfc822: {} - {} - {}", - wrapper.date(), - wrapper.field_from_to_string(), - wrapper.subject() - ), - Err(e) => write!(f, "{}", e), - }, + ContentType::MessageRfc822 => { + match EnvelopeWrapper::new(self.body.display_bytes(&self.raw).to_vec()) { + Ok(wrapper) => write!( + f, + "message/rfc822: {} - {} - {}", + wrapper.date(), + wrapper.field_from_to_string(), + wrapper.subject() + ), + Err(e) => write!(f, "{}", e), + } + } ContentType::PGPSignature => write!(f, "pgp signature {}", self.mime_type()), ContentType::OctetStream { ref name } => { write!(f, "{}", name.clone().unwrap_or_else(|| self.mime_type())) @@ -271,7 +320,7 @@ impl fmt::Display for Attachment { ContentType::Other { .. } => write!(f, "Data attachment of type {}", self.mime_type()), ContentType::Text { .. } => write!(f, "Text attachment of type {}", self.mime_type()), ContentType::Multipart { - subattachments: ref sub_att_vec, + parts: ref sub_att_vec, .. } => write!( f, @@ -292,6 +341,10 @@ impl Attachment { Attachment { content_type, content_transfer_encoding, + body: StrBuilder { + length: raw.len(), + offset: 0, + }, raw, } } @@ -300,6 +353,62 @@ impl Attachment { &self.raw } + pub fn body(&self) -> &[u8] { + self.body.display_bytes(&self.raw) + } + + pub fn part_boundaries(&self) -> Vec { + if self.raw.is_empty() { + return Vec::new(); + } + + match self.content_type { + ContentType::Multipart { ref boundary, .. } => { + match parser::multipart_parts(self.body(), boundary).to_full_result() { + Ok(v) => v, + Err(e) => { + debug!("error in parsing attachment"); + debug!("\n-------------------------------"); + debug!("{}\n", ::std::string::String::from_utf8_lossy(&self.raw)); + debug!("-------------------------------\n"); + debug!("{:?}\n", e); + Vec::new() + } + } + } + _ => Vec::new(), + } + } + + /* Call on the body of a multipart/mixed Envelope to check if there are attachments without + * completely parsing them */ + pub fn check_if_has_attachments_quick(bytes: &[u8], boundary: &[u8]) -> bool { + if bytes.is_empty() { + return false; + } + // FIXME: check if any part is multipart/mixed as well + + match parser::multipart_parts(bytes, boundary).to_full_result() { + Ok(parts) => { + for p in parts { + for (n, v) in crate::email::parser::HeaderIterator(p.display_bytes(bytes)) { + if !n.eq_ignore_ascii_case(b"content-type") && !v.starts_with(b"text/") { + return true; + } + } + } + } + Err(e) => { + debug!("error in parsing multipart_parts"); + debug!("\n-------------------------------"); + debug!("{}\n", ::std::string::String::from_utf8_lossy(bytes)); + debug!("-------------------------------\n"); + debug!("{:?}\n", e); + } + } + false + } + fn get_text_recursive(&self, text: &mut Vec) { match self.content_type { ContentType::Text { .. } => { @@ -307,11 +416,11 @@ impl Attachment { } ContentType::Multipart { ref kind, - ref subattachments, + ref parts, .. } => match kind { MultipartType::Alternative => { - for a in subattachments { + for a in parts { if let ContentType::Text { kind: Text::Plain, .. } = a.content_type @@ -322,7 +431,7 @@ impl Attachment { } } _ => { - for a in subattachments { + for a in parts { a.get_text_recursive(text) } } @@ -331,7 +440,7 @@ impl Attachment { } } pub fn text(&self) -> String { - let mut text = Vec::with_capacity(self.raw.len()); + let mut text = Vec::with_capacity(self.body.length); self.get_text_recursive(&mut text); String::from_utf8_lossy(text.as_slice().trim()).into() } @@ -346,7 +455,7 @@ impl Attachment { fn count_recursive(att: &Attachment, ret: &mut Vec) { match att.content_type { ContentType::Multipart { - subattachments: ref sub_att_vec, + parts: ref sub_att_vec, .. } => { ret.push(att.clone()); @@ -387,10 +496,10 @@ impl Attachment { } => false, ContentType::Multipart { kind: MultipartType::Alternative, - ref subattachments, + ref parts, .. } => { - for a in subattachments.iter() { + for a in parts.iter() { if let ContentType::Text { kind: Text::Plain, .. } = a.content_type @@ -402,18 +511,15 @@ impl Attachment { } ContentType::Multipart { kind: MultipartType::Signed, - ref subattachments, + ref parts, .. - } => subattachments + } => parts .iter() .find(|s| s.content_type != ContentType::PGPSignature) .map(Attachment::is_html) .unwrap_or(false), - ContentType::Multipart { - ref subattachments, .. - } => subattachments - .iter() - .fold(true, |acc, a| match &a.content_type { + ContentType::Multipart { ref parts, .. } => { + parts.iter().fold(true, |acc, a| match &a.content_type { ContentType::Text { kind: Text::Plain, .. } => false, @@ -425,7 +531,8 @@ impl Attachment { .. } => a.is_html(), _ => acc, - }), + }) + } _ => false, } } @@ -454,16 +561,16 @@ fn decode_rec_helper<'a>(a: &'a Attachment, filter: &mut Option>) -> .into_bytes(), ContentType::PGPSignature => a.content_type.to_string().into_bytes(), ContentType::MessageRfc822 => { - let temp = decode_rfc822(&a.raw); + let temp = decode_rfc822(a.body()); decode_rec(&temp, None) } ContentType::Multipart { ref kind, - ref subattachments, + ref parts, .. } => match kind { MultipartType::Alternative => { - for a in subattachments { + for a in parts { if let ContentType::Text { kind: Text::Plain, .. } = a.content_type @@ -475,7 +582,7 @@ fn decode_rec_helper<'a>(a: &'a Attachment, filter: &mut Option>) -> } _ => { let mut vec = Vec::new(); - for a in subattachments { + for a in parts { vec.extend(decode_rec_helper(a, filter)); } vec @@ -495,23 +602,23 @@ fn decode_helper<'a>(a: &'a Attachment, filter: &mut Option>) -> Vec< }; let bytes = match a.content_transfer_encoding { - ContentTransferEncoding::Base64 => match BASE64_MIME.decode(a.raw()) { + ContentTransferEncoding::Base64 => match BASE64_MIME.decode(a.body()) { Ok(v) => v, - _ => a.raw().to_vec(), + _ => a.body().to_vec(), }, - ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_bytes(a.raw()) + ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_bytes(a.body()) .to_full_result() .unwrap(), ContentTransferEncoding::_7Bit | ContentTransferEncoding::_8Bit - | ContentTransferEncoding::Other { .. } => a.raw().to_vec(), + | ContentTransferEncoding::Other { .. } => a.body().to_vec(), }; let mut ret = if a.content_type.is_text() { if let Ok(v) = parser::decode_charset(&bytes, charset) { v.into_bytes() } else { - a.raw().to_vec() + a.body().to_vec() } } else { bytes.to_vec() diff --git a/melib/src/email/compose.rs b/melib/src/email/compose.rs index 26ca32193..29a62e454 100644 --- a/melib/src/email/compose.rs +++ b/melib/src/email/compose.rs @@ -259,13 +259,13 @@ impl Draft { ret.push_str("MIME-Version: 1.0\n"); if !self.attachments.is_empty() { - let mut subattachments = Vec::with_capacity(self.attachments.len() + 1); + let mut parts = Vec::with_capacity(self.attachments.len() + 1); let attachments = std::mem::replace(&mut self.attachments, Vec::new()); let mut body_attachment = AttachmentBuilder::default(); body_attachment.set_raw(self.body.as_bytes().to_vec()); - subattachments.push(body_attachment); - subattachments.extend(attachments.into_iter()); - build_multipart(&mut ret, MultipartType::Mixed, subattachments); + parts.push(body_attachment); + parts.extend(attachments.into_iter()); + build_multipart(&mut ret, MultipartType::Mixed, parts); } else { if self.body.is_ascii() { ret.push('\n'); @@ -309,9 +309,9 @@ fn ignore_header(header: &[u8]) -> bool { } } -fn build_multipart(ret: &mut String, kind: MultipartType, subattachments: Vec) { +fn build_multipart(ret: &mut String, kind: MultipartType, parts: Vec) { use ContentType::*; - let boundary = ContentType::make_boundary(&subattachments); + let boundary = ContentType::make_boundary(&parts); ret.extend( format!( "Content-Type: {}; charset=\"utf-8\"; boundary=\"{}\"\n", @@ -322,7 +322,7 @@ fn build_multipart(ret: &mut String, kind: MultipartType, subattachments: Vec { build_multipart( ret, kind, - subsubattachments + subparts .into_iter() .map(|s| s.into()) .collect::>(), diff --git a/melib/src/email/parser.rs b/melib/src/email/parser.rs index 28a3c9401..9938b10b3 100644 --- a/melib/src/email/parser.rs +++ b/melib/src/email/parser.rs @@ -28,6 +28,17 @@ pub(super) use nom::{ErrorKind, IResult, Needed}; use encoding::all::*; use std; +macro_rules! is_ctl_or_space { + ($var:ident) => { + /* */ + $var < 33 || $var == 127 + }; + ($var:expr) => { + /* */ + $var < 33 || $var == 127 + }; +} + macro_rules! is_whitespace { ($var:ident) => { $var == b' ' || $var == b'\t' || $var == b'\n' || $var == b'\r' @@ -138,11 +149,14 @@ fn header_with_val(input: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> { } let mut ptr = 0; let mut name: &[u8] = &input[0..0]; + /* field-name = 1* */ for (i, x) in input.iter().enumerate() { if *x == b':' { name = &input[0..i]; ptr = i + 1; break; + } else if is_ctl_or_space!(*x) { + return IResult::Error(error_code!(ErrorKind::Custom(43))); } } if name.is_empty() { @@ -184,6 +198,8 @@ fn header_without_val(input: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> { } let mut ptr = 0; let mut name: &[u8] = &input[0..0]; + let mut has_colon = false; + /* field-name = 1* */ for (i, x) in input.iter().enumerate() { if input[i..].starts_with(b"\r\n") { name = &input[0..i]; @@ -191,8 +207,11 @@ fn header_without_val(input: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> { break; } else if *x == b':' || *x == b'\n' { name = &input[0..i]; + has_colon = true; ptr = i; break; + } else if is_ctl_or_space!(*x) { + return IResult::Error(error_code!(ErrorKind::Custom(43))); } } if name.is_empty() { @@ -200,10 +219,16 @@ fn header_without_val(input: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> { } if input[ptr] == b':' { ptr += 1; + has_colon = true; if ptr >= input.len() { return IResult::Incomplete(Needed::Unknown); } } + + if !has_colon { + return IResult::Incomplete(Needed::Unknown); + } + while input[ptr] == b' ' { ptr += 1; if ptr >= input.len() { @@ -265,10 +290,10 @@ named!(pub body_raw<&[u8]>, named!(pub mail<(std::vec::Vec<(&[u8], &[u8])>, &[u8])>, separated_pair!(headers, alt_complete!(tag!(b"\n") | tag!(b"\r\n")), take_while!(call!(|_| true)))); + named!(pub attachment<(std::vec::Vec<(&[u8], &[u8])>, &[u8])>, do_parse!( - opt!(is_a!(" \n\t\r")) >> - pair: pair!(many0!(complete!(header)), take_while!(call!(|_| true))) >> + pair: separated_pair!(many0!(complete!(header)), alt_complete!(tag!(b"\n") | tag!(b"\r\n")), take_while!(call!(|_| true))) >> ( { pair } ))); /* Header parsers */ @@ -636,9 +661,73 @@ fn message_id_peek(input: &[u8]) -> IResult<&[u8], &[u8]> { named!(pub references>, separated_list!(complete!(is_a!(" \n\t\r")), message_id_peek)); -fn attachments_f<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec<&'a [u8]>> { +pub fn multipart_parts<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec> { + let mut ret: Vec<_> = Vec::new(); + let mut input = input; + let mut offset = 0; + loop { + let b_start = if let Some(v) = input.find(boundary) { + v + } else { + return IResult::Error(error_code!(ErrorKind::Custom(39))); + }; + + if b_start < 2 { + return IResult::Error(error_code!(ErrorKind::Custom(40))); + } + offset += b_start - 2; + input = &input[b_start - 2..]; + if &input[0..2] == b"--" { + offset += 2 + boundary.len(); + input = &input[2 + boundary.len()..]; + if input[0] == b'\n' { + offset += 1; + input = &input[1..]; + } else if input[0..].starts_with(b"\r\n") { + offset += 2; + input = &input[2..]; + } else { + continue; + } + break; + } + } + + loop { + if input.len() < boundary.len() + 4 { + return IResult::Error(error_code!(ErrorKind::Custom(41))); + } + if let Some(end) = input.find(boundary) { + if &input[end - 2..end] != b"--" { + return IResult::Error(error_code!(ErrorKind::Custom(42))); + } + ret.push(StrBuilder { + offset, + length: end - 2, + }); + offset += end + boundary.len(); + input = &input[end + boundary.len()..]; + if input.len() < 2 || input[0] != b'\n' || &input[0..2] == b"--" { + break; + } + if input[0] == b'\n' { + offset += 1; + input = &input[1..]; + } else if input[0..].starts_with(b"\r\n") { + offset += 2; + input = &input[2..]; + } + continue; + } else { + return IResult::Error(error_code!(ErrorKind::Custom(43))); + } + } + IResult::Done(input, ret) +} + +fn parts_f<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec<&'a [u8]>> { let mut ret: Vec<&[u8]> = Vec::new(); - let mut input = input.ltrim(); + let mut input = input; loop { let b_start = if let Some(v) = input.find(boundary) { v @@ -652,10 +741,13 @@ fn attachments_f<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec< input = &input[b_start - 2..]; if &input[0..2] == b"--" { input = &input[2 + boundary.len()..]; - if input[0] != b'\n' && !input[0..].starts_with(b"\r\n") { + if input[0] == b'\n' { + input = &input[1..]; + } else if input[0..].starts_with(b"\r\n") { + input = &input[2..]; + } else { continue; } - input = &input[1..]; break; } } @@ -672,7 +764,11 @@ fn attachments_f<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec< if input.len() < 2 || input[0] != b'\n' || &input[0..2] == b"--" { break; } - input = &input[1..]; + if input[0] == b'\n' { + input = &input[1..]; + } else if input[0..].starts_with(b"\r\n") { + input = &input[2..]; + } continue; } else { return IResult::Error(error_code!(ErrorKind::Custom(43))); @@ -681,8 +777,8 @@ fn attachments_f<'a>(input: &'a [u8], boundary: &[u8]) -> IResult<&'a [u8], Vec< IResult::Done(input, ret) } -named_args!(pub attachments<'a>(boundary: &'a [u8]) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >, - alt_complete!(call!(attachments_f, boundary) | do_parse!( +named_args!(pub parts<'a>(boundary: &'a [u8]) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >, + alt_complete!(call!(parts_f, boundary) | do_parse!( take_until_and_consume!(&b"--"[..]) >> take_until_and_consume!(boundary) >> ( { Vec::<&[u8]>::new() } )) @@ -890,6 +986,30 @@ pub fn mailto(mut input: &[u8]) -> IResult<&[u8], Mailto> { ) } +//alt_complete!(call!(header_without_val) | call!(header_with_val)) + +pub struct HeaderIterator<'a>(pub &'a [u8]); + +impl<'a> Iterator for HeaderIterator<'a> { + type Item = (&'a [u8], &'a [u8]); + fn next(&mut self) -> Option<(&'a [u8], &'a [u8])> { + if self.0.is_empty() { + return None; + } + + match header(self.0) { + IResult::Done(rest, value) => { + self.0 = rest; + Some(value) + } + _ => { + self.0 = &[]; + None + } + } + } +} + #[cfg(test)] mod tests { @@ -1009,7 +1129,7 @@ mod tests { Ok(v) => v, Err(_) => panic!(), }; - let attachments = attachments(body, boundary).to_full_result().unwrap(); + let attachments = parts(body, boundary).to_full_result().unwrap(); assert_eq!(attachments.len(), 4); let v: Vec<&str> = attachments .iter() diff --git a/ui/src/components/mail/view.rs b/ui/src/components/mail/view.rs index 3bf69b2e0..5385e0356 100644 --- a/ui/src/components/mail/view.rs +++ b/ui/src/components/mail/view.rs @@ -213,7 +213,7 @@ impl MailView { } t } - ViewMode::Raw => String::from_utf8_lossy(body.raw()).into_owned(), + ViewMode::Raw => String::from_utf8_lossy(body.body()).into_owned(), ViewMode::Url => { let mut t = body_text.to_string(); for (lidx, l) in finder.links(&body.text()).enumerate() { @@ -745,7 +745,7 @@ impl Component for MailView { if let Some(u) = envelope.body(op).attachments().get(lidx) { match u.content_type() { ContentType::MessageRfc822 => { - match EnvelopeWrapper::new(u.raw().to_vec()) { + match EnvelopeWrapper::new(u.body().to_vec()) { Ok(wrapper) => { context.replies.push_back(UIEvent::Action(Tab(New(Some( Box::new(EnvelopeView::new( diff --git a/ui/src/components/mail/view/envelope.rs b/ui/src/components/mail/view/envelope.rs index a9cfd6265..d6fd8146f 100644 --- a/ui/src/components/mail/view/envelope.rs +++ b/ui/src/components/mail/view/envelope.rs @@ -150,7 +150,7 @@ impl EnvelopeView { } t } - ViewMode::Raw => String::from_utf8_lossy(body.raw()).into_owned(), + ViewMode::Raw => String::from_utf8_lossy(body.body()).into_owned(), ViewMode::Url => { let mut t = body_text.to_string(); for (lidx, l) in finder.links(&body.text()).enumerate() {