summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--melib/src/email.rs47
-rw-r--r--melib/src/email/address.rs7
-rw-r--r--melib/src/email/attachment_types.rs7
-rw-r--r--melib/src/email/attachments.rs201
-rw-r--r--melib/src/email/compose.rs18
-rw-r--r--melib/src/email/parser.rs140
-rw-r--r--ui/src/components/mail/view.rs4
-rw-r--r--ui/src/components/mail/view/envelope.rs2
8 files changed, 320 insertions, 106 deletions
diff --git a/melib/src/email.rs b/melib/src/email.rs
index f441cb32..4c4dcd05 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<Vec<(&'a str, &'a str)>> {
diff --git a/melib/src/email/address.rs b/melib/src/email/address.rs
index dd771d92..4845d328 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 307474d5..f5287df6 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<u8>,
kind: MultipartType,
- subattachments: Vec<Attachment>,
+ parts: Vec<Attachment>,
},
MessageRfc822,
PGPSignature,
@@ -178,7 +179,7 @@ impl ContentType {
}
}
- pub fn make_boundary(subattachments: &Vec<AttachmentBuilder>) -> String {
+ pub fn make_boundary(parts: &Vec<AttachmentBuilder>) -> 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 bea56b18..06cbf4f4 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 <http://www.gnu.org/licenses/>.
*/
+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<u8>,
+ 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<u8>) -> &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<Attachment> {
+ pub fn parts(raw: &[u8], boundary: &[u8]) -> Vec<Attachment> {
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<Attachment> for AttachmentBuilder {
content_type,
content_transfer_encoding,
raw,
+ body,
} = val;
AttachmentBuilder {
content_type,
content_transfer_encoding,
raw,
+ body,
}
}
}
@@ -230,10 +276,11 @@ impl From<Attachment> 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<u8>,
+ pub raw: Vec<u8>,
+ 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<StrBuilder> {
+ 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<u8>) {
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<Attachment>) {
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<Filter<'a>>) ->
.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<Filter<'a>>) ->
}
_ => {
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<Filter<'a>>) -> 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 26ca3219..29a62e45 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<AttachmentBuilder>) {
+fn build_multipart(ret: &mut String, kind: MultipartType, parts: Vec<AttachmentBuilder>) {
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<At
ret.push('\n');
/* rfc1341 */
ret.extend("This is a MIME formatted message with attachments. Use a MIME-compliant client to view it properly.\n".chars());
- for sub in subattachments {
+ for sub in parts {
ret.push_str("--");
ret.extend(boundary.chars());
ret.push('\n');
@@ -347,12 +347,12 @@ fn build_multipart(ret: &mut String, kind: MultipartType, subattachments: Vec<At
Multipart {
boundary: _boundary,
kind,
- subattachments: subsubattachments,
+ parts: subparts,
} => {
build_multipart(
ret,
kind,
- subsubattachments
+ subparts
.into_iter()
.map(|s| s.into())
.collect::<Vec<AttachmentBuilder>>(),
diff --git a/melib/src/email/parser.rs b/melib/src/email/parser.rs
index 28a3c940..9938b10b 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) => {
+ /* <any ASCII control character and DEL> */
+ $var < 33 || $var == 127
+ };
+ ($var:expr) => {
+ /* <any ASCII control character and DEL> */
+ $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*<any CHAR, excluding CTLs, SPACE, and ":"> */
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*<any CHAR, excluding CTLs, SPACE, and ":"> */
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<Vec<&[u8]>>, 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<StrBuilder>> {
+ 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 3bf69b2e..5385e035 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 a9cfd626..d6fd8146 100644
--- a/ui/src/components/mail/view/envelope.rs
+++ b/ui/src/components/mail/view/envelope.rs
@@ -150,7 +150,7 @@ impl EnvelopeView {