Remove some string conversions in parsing

embed
Manos Pitsidianakis 2018-08-04 20:40:20 +03:00
parent d5c0542f61
commit e91f22cb4f
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
5 changed files with 226 additions and 183 deletions

View File

@ -19,6 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use mailbox::email::parser;
use mailbox::email::parser::BytesExt;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::str;
@ -28,7 +29,7 @@ use data_encoding::BASE64_MIME;
/*
*
* Data
* Text { content: String }
* Text { content: Vec<u8> }
* Multipart
*/
@ -37,7 +38,7 @@ pub enum MultipartType {
Mixed,
Alternative,
Digest,
Unsupported { tag: String },
Unsupported { tag: Vec<u8> },
}
impl Display for MultipartType {
@ -46,7 +47,7 @@ impl Display for MultipartType {
MultipartType::Mixed => write!(f, "multipart/mixed"),
MultipartType::Alternative => write!(f, "multipart/alternative"),
MultipartType::Digest => write!(f, "multipart/digest"),
MultipartType::Unsupported { tag: ref t } => write!(f, "multipart/{}", t),
MultipartType::Unsupported { tag: ref t } => write!(f, "multipart/{}", String::from_utf8_lossy(t)),
}
}
}
@ -54,10 +55,10 @@ impl Display for MultipartType {
#[derive(Clone, Debug)]
pub enum AttachmentType {
Data {
tag: String,
tag: Vec<u8>,
},
Text {
content: String,
content: Vec<u8>,
},
Multipart {
of_type: MultipartType,
@ -68,8 +69,8 @@ pub enum AttachmentType {
impl Display for AttachmentType {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match self {
AttachmentType::Data { tag: ref t } => write!(f, "{}", t),
AttachmentType::Text { content: ref c } => write!(f, "{}", c),
AttachmentType::Data { tag: ref t } => write!(f, "{}", String::from_utf8_lossy(t)),
AttachmentType::Text { content: ref c } => write!(f, "{}", String::from_utf8_lossy(c)),
AttachmentType::Multipart { of_type: ref t, .. } => write!(f, "{}", t),
}
}
@ -77,8 +78,8 @@ impl Display for AttachmentType {
#[derive(Clone, Debug)]
pub enum ContentType {
Text,
Multipart { boundary: String },
Unsupported { tag: String },
Multipart { boundary: Vec<u8> },
Unsupported { tag: Vec<u8> },
}
impl Display for ContentType {
@ -86,20 +87,20 @@ impl Display for ContentType {
match *self {
ContentType::Text => write!(f, "text"),
ContentType::Multipart { .. } => write!(f, "multipart"),
ContentType::Unsupported { tag: ref t } => write!(f, "{}", t),
ContentType::Unsupported { tag: ref t } => write!(f, "{}", String::from_utf8_lossy(t)),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ContentSubType {
Plain,
Other { tag: String },
Other { tag: Vec<u8> },
}
impl Display for ContentSubType {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match *self {
ContentSubType::Plain => write!(f, "plain"),
ContentSubType::Other { tag: ref t } => write!(f, "{}", t),
ContentSubType::Other { tag: ref t } => write!(f, "{}", String::from_utf8_lossy(t)),
}
}
}
@ -109,7 +110,7 @@ pub enum ContentTransferEncoding {
_7Bit,
Base64,
QuotedPrintable,
Other { tag: String },
Other { tag: Vec<u8> },
}
/// TODO: Add example.
@ -129,13 +130,17 @@ impl AttachmentBuilder {
raw: content.to_vec(),
}
}
pub fn content_type(&mut self, value: &str) -> &Self {
match parser::content_type(value.as_bytes()).to_full_result() {
Ok((ct, cst, params)) => if ct.eq_ignore_ascii_case("multipart") {
pub fn content_type(&mut self, value: &[u8]) -> &Self {
match parser::content_type(value).to_full_result() {
Ok((ct, cst, params)) => if ct.eq_ignore_ascii_case(b"multipart") {
let mut boundary = None;
for (n, v) in params {
if n.eq_ignore_ascii_case("boundary") {
boundary = Some(format!("--{}--", v).to_string());
if n.eq_ignore_ascii_case(b"boundary") {
let mut vec: Vec<u8> = Vec::with_capacity(v.len()+4);
vec.extend_from_slice(b"--");
vec.extend(v);
vec.extend_from_slice(b"--");
boundary = Some(vec);
break;
}
}
@ -144,11 +149,11 @@ impl AttachmentBuilder {
boundary: boundary.unwrap(),
};
self.content_type.1 = ContentSubType::Other {
tag: cst.to_string(),
tag: cst.into(),
};
} else if ct.eq_ignore_ascii_case("text") {
} else if ct.eq_ignore_ascii_case(b"text") {
self.content_type.0 = ContentType::Text;
if !cst.eq_ignore_ascii_case("plain") {
if !cst.eq_ignore_ascii_case(b"plain") {
self.content_type.1 = ContentSubType::Other {
tag: cst.to_ascii_lowercase(),
};
@ -167,14 +172,14 @@ impl AttachmentBuilder {
}
self
}
pub fn content_transfer_encoding(&mut self, value: &str) -> &Self {
self.content_transfer_encoding = if value.eq_ignore_ascii_case("base64") {
pub fn content_transfer_encoding(&mut self, value: &[u8]) -> &Self {
self.content_transfer_encoding = if value.eq_ignore_ascii_case(b"base64") {
ContentTransferEncoding::Base64
} else if value.eq_ignore_ascii_case("7bit") {
} else if value.eq_ignore_ascii_case(b"7bit") {
ContentTransferEncoding::_7Bit
} else if value.eq_ignore_ascii_case("8bit") {
} else if value.eq_ignore_ascii_case(b"8bit") {
ContentTransferEncoding::_8Bit
} else if value.eq_ignore_ascii_case("quoted-printable") {
} else if value.eq_ignore_ascii_case(b"quoted-printable") {
ContentTransferEncoding::QuotedPrintable
} else {
ContentTransferEncoding::Other {
@ -183,7 +188,7 @@ impl AttachmentBuilder {
};
self
}
fn decode(&self) -> String {
fn decode(&self) -> Vec<u8> {
// TODO: Use charset for decoding
match self.content_transfer_encoding {
ContentTransferEncoding::Base64 => match BASE64_MIME.decode(
@ -197,15 +202,17 @@ impl AttachmentBuilder {
})
.as_bytes(),
) {
Ok(ref v) => {
let s = String::from_utf8_lossy(v);
if s.find("\r\n").is_some() {
s.replace("\r\n", "\n")
} else {
s.into_owned()
Ok(ref s) => {
let s:Vec<u8> = s.clone();
{
let slice = &s[..];
if slice.find(b"\r\n").is_some() {
s.replace(b"\r\n", b"\n");
}
}
s
}
_ => String::from_utf8_lossy(&self.raw).into_owned(),
_ => self.raw.clone()
},
ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_text(&self.raw)
.to_full_result()
@ -213,7 +220,7 @@ impl AttachmentBuilder {
ContentTransferEncoding::_7Bit
| ContentTransferEncoding::_8Bit
| ContentTransferEncoding::Other { .. } => {
String::from_utf8_lossy(&self.raw).into_owned()
self.raw.clone()
}
}
}
@ -224,11 +231,11 @@ impl AttachmentBuilder {
},
ContentType::Multipart { boundary: ref b } => {
let multipart_type = match self.content_type.1 {
ContentSubType::Other { ref tag } => match tag.as_ref() {
"mixed" => MultipartType::Mixed,
"alternative" => MultipartType::Alternative,
"digest" => MultipartType::Digest,
t => MultipartType::Unsupported { tag: t.to_string() },
ContentSubType::Other { ref tag } => match &tag[..] {
b"mixed" => MultipartType::Mixed,
b"alternative" => MultipartType::Alternative,
b"digest" => MultipartType::Digest,
_ => MultipartType::Unsupported { tag:tag.clone() },
},
_ => panic!(),
};
@ -247,7 +254,7 @@ impl AttachmentBuilder {
}
}
pub fn subattachments(raw: &[u8], boundary: &str) -> Vec<Attachment> {
pub fn subattachments(raw: &[u8], boundary: &[u8]) -> Vec<Attachment> {
let boundary_length = boundary.len();
match parser::attachments(raw, &boundary[0..boundary_length - 2], boundary).to_full_result()
{
@ -267,9 +274,9 @@ impl AttachmentBuilder {
};
let mut builder = AttachmentBuilder::new(body);
for (name, value) in headers {
if name.eq_ignore_ascii_case("content-type") {
if name.eq_ignore_ascii_case(b"content-type") {
builder.content_type(value);
} else if name.eq_ignore_ascii_case("content-transfer-encoding") {
} else if name.eq_ignore_ascii_case(b"content-transfer-encoding") {
builder.content_transfer_encoding(value);
}
}
@ -333,7 +340,7 @@ impl Attachment {
//text.push_str(&format!("Data attachment of type {}", self.mime_type()));
}
AttachmentType::Text { content: ref t } => {
text.push_str(t);
text.push_str(&String::from_utf8_lossy(t));
}
AttachmentType::Multipart {
of_type: ref multipart_type,

View File

@ -22,6 +22,7 @@
pub mod attachments;
pub mod parser;
use parser::BytesExt;
pub use self::attachments::*;
use error::{MeliError, Result};
use mailbox::backends::BackendOpGenerator;
@ -31,20 +32,21 @@ use std::fmt;
use std::option::Option;
use std::string::String;
use std::sync::Arc;
use std::borrow::Cow;
use chrono;
use chrono::TimeZone;
#[derive(Clone, Debug)]
pub struct GroupAddress {
raw: String,
raw: Vec<u8>,
display_name: StrBuilder,
mailbox_list: Vec<Address>,
}
#[derive(Clone, Debug)]
pub struct MailboxAddress {
raw: String,
raw: Vec<u8>,
display_name: StrBuilder,
address_spec: StrBuilder,
}
@ -110,42 +112,42 @@ struct StrBuilder {
/// Structs implementing this trait must contain a `StrBuilder` field.
pub trait StrBuild {
/// Create a new `Self` out of a string and a slice
fn new(string: &str, slice: &str) -> Self;
fn new(string: &[u8], slice: &[u8]) -> Self;
/// Get the slice part of the string
fn raw(&self) -> &str;
fn raw(&self) -> &[u8];
/// Get the entire string as a slice
fn val(&self) -> &str;
fn val(&self) -> &[u8];
}
impl StrBuilder {
fn display<'a>(&self, s: &'a str) -> &'a str {
fn display<'a>(&self, s: &'a [u8]) -> String {
let offset = self.offset;
let length = self.length;
&s[offset..offset + length]
String::from_utf8(s[offset..offset + length].to_vec()).unwrap()
}
}
/// `MessageID` is accessed through the `StrBuild` trait.
#[derive(Clone)]
pub struct MessageID(String, StrBuilder);
pub struct MessageID(Vec<u8>, StrBuilder);
impl StrBuild for MessageID {
fn new(string: &str, slice: &str) -> Self {
fn new(string: &[u8], slice: &[u8]) -> Self {
let offset = string.find(slice).unwrap();
MessageID(
string.to_string(),
string.to_owned(),
StrBuilder {
offset: offset,
length: slice.len() + 1,
},
)
}
fn raw(&self) -> &str {
fn raw(&self) -> &[u8] {
let offset = self.1.offset;
let length = self.1.length;
&self.0[offset..offset + length - 1]
}
fn val(&self) -> &str {
fn val(&self) -> &[u8] {
&self.0
}
}
@ -173,13 +175,13 @@ impl PartialEq for MessageID {
}
impl fmt::Debug for MessageID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.raw())
write!(f, "{}", String::from_utf8(self.raw().to_vec()).unwrap())
}
}
#[derive(Clone, Debug)]
struct References {
raw: String,
raw: Vec<u8>,
refs: Vec<MessageID>,
}
@ -208,7 +210,7 @@ pub struct Envelope {
from: Vec<Address>,
to: Vec<Address>,
body: Option<Attachment>,
subject: Option<String>,
subject: Option<Vec<u8>>,
message_id: Option<MessageID>,
in_reply_to: Option<MessageID>,
references: Option<References>,
@ -268,48 +270,48 @@ impl Envelope {
if value.len() == 1 && value.is_empty() {
continue;
}
if name.eq_ignore_ascii_case("to") {
let parse_result = parser::rfc2822address_list(value.as_bytes());
if name.eq_ignore_ascii_case(b"to") {
let parse_result = parser::rfc2822address_list(value);
let value = if parse_result.is_done() {
parse_result.to_full_result().unwrap()
} else {
Vec::new()
};
self.set_to(value);
} else if name.eq_ignore_ascii_case("from") {
let parse_result = parser::rfc2822address_list(value.as_bytes());
} else if name.eq_ignore_ascii_case(b"from") {
let parse_result = parser::rfc2822address_list(value);
let value = if parse_result.is_done() {
parse_result.to_full_result().unwrap()
} else {
Vec::new()
};
self.set_from(value);
} else if name.eq_ignore_ascii_case("subject") {
let parse_result = parser::phrase(value.trim().as_bytes());
} else if name.eq_ignore_ascii_case(b"subject") {
let parse_result = parser::phrase(value.trim());
let value = if parse_result.is_done() {
parse_result.to_full_result().unwrap()
} else {
"".to_string()
"".into()
};
self.set_subject(value);
} else if name.eq_ignore_ascii_case("message-id") {
} else if name.eq_ignore_ascii_case(b"message-id") {
self.set_message_id(value);
} else if name.eq_ignore_ascii_case("references") {
} else if name.eq_ignore_ascii_case(b"references") {
{
let parse_result = parser::references(value.as_bytes());
let parse_result = parser::references(value);
if parse_result.is_done() {
for v in parse_result.to_full_result().unwrap() {
self.push_references(v);
}
}
}
self.set_references(value.to_string());
} else if name.eq_ignore_ascii_case("in-reply-to") {
self.set_references(value);
} else if name.eq_ignore_ascii_case(b"in-reply-to") {
self.set_in_reply_to(value);
in_reply_to = Some(value);
} else if name.eq_ignore_ascii_case("date") {
self.set_date(value.to_string());
datetime = Some(value.to_string());
} else if name.eq_ignore_ascii_case(b"date") {
self.set_date(value);
datetime = Some(value);
}
}
/*
@ -374,46 +376,46 @@ impl Envelope {
if value.len() == 1 && value.is_empty() {
continue;
}
if name.eq_ignore_ascii_case("content-transfer-encoding") {
if name.eq_ignore_ascii_case(b"content-transfer-encoding") {
builder.content_transfer_encoding(value);
} else if name.eq_ignore_ascii_case("content-type") {
} else if name.eq_ignore_ascii_case(b"content-type") {
builder.content_type(value);
}
}
builder.build()
}
pub fn subject(&self) -> &str {
pub fn subject(&self) -> Cow<str> {
match self.subject {
Some(ref s) => s,
_ => "",
Some(ref s) => String::from_utf8_lossy(s),
_ => Cow::from(String::new()),
}
}
pub fn in_reply_to(&self) -> &str {
pub fn in_reply_to(&self) -> Cow<str> {
match self.in_reply_to {
Some(ref s) => s.val(),
_ => "",
Some(ref s) => String::from_utf8_lossy(s.val()),
_ => Cow::from(String::new()),
}
}
pub fn in_reply_to_raw(&self) -> &str {
pub fn in_reply_to_raw(&self) -> Cow<str> {
match self.in_reply_to {
Some(ref s) => s.raw(),
_ => "",
Some(ref s) => String::from_utf8_lossy(s.raw()).into(),
_ => Cow::from(String::new()),
}
}
pub fn message_id(&self) -> &str {
pub fn message_id(&self) -> Cow<str> {
match self.message_id {
Some(ref s) => s.val(),
_ => "",
Some(ref s) => String::from_utf8_lossy(s.val()),
_ => Cow::from(String::new()),
}
}
pub fn message_id_raw(&self) -> &str {
pub fn message_id_raw(&self) -> Cow<str> {
match self.message_id {
Some(ref s) => s.raw(),
_ => "",
Some(ref s) => String::from_utf8_lossy(s.raw()),
_ => Cow::from(String::new()),
}
}
fn set_date(&mut self, new_val: String) -> () {
self.date = new_val;
fn set_date(&mut self, new_val: &[u8]) -> () {
self.date = String::from_utf8_lossy(new_val).into_owned();
}
fn set_from(&mut self, new_val: Vec<Address>) -> () {
self.from = new_val;
@ -421,8 +423,8 @@ impl Envelope {
fn set_to(&mut self, new_val: Vec<Address>) -> () {
self.to = new_val;
}
fn set_in_reply_to(&mut self, new_val: &str) -> () {
let slice = match parser::message_id(new_val.as_bytes()).to_full_result() {
fn set_in_reply_to(&mut self, new_val: &[u8]) -> () {
let slice = match parser::message_id(new_val).to_full_result() {
Ok(v) => v,
Err(_) => {
self.in_reply_to = None;
@ -431,11 +433,11 @@ impl Envelope {
};
self.in_reply_to = Some(MessageID::new(new_val, slice));
}
fn set_subject(&mut self, new_val: String) -> () {
fn set_subject(&mut self, new_val: Vec<u8>) -> () {
self.subject = Some(new_val);
}
fn set_message_id(&mut self, new_val: &str) -> () {
let slice = match parser::message_id(new_val.as_bytes()).to_full_result() {
fn set_message_id(&mut self, new_val: &[u8]) -> () {
let slice = match parser::message_id(new_val).to_full_result() {
Ok(v) => v,
Err(_) => {
self.message_id = None;
@ -444,8 +446,8 @@ impl Envelope {
};
self.message_id = Some(MessageID::new(new_val, slice));
}
fn push_references(&mut self, new_val: &str) -> () {
let slice = match parser::message_id(new_val.as_bytes()).to_full_result() {
fn push_references(&mut self, new_val: &[u8]) -> () {
let slice = match parser::message_id(new_val).to_full_result() {
Ok(v) => v,
Err(_) => {
return;
@ -471,21 +473,22 @@ impl Envelope {
let mut v = Vec::new();
v.push(new_ref);
self.references = Some(References {
raw: "".to_string(),
raw: "".into(),
refs: v,
});
}
}
}
fn set_references(&mut self, new_val: String) -> () {
// TODO: Check what references should be like again.
fn set_references(&mut self, new_val: &[u8]) -> () {
match self.references {
Some(ref mut s) => {
s.raw = new_val;
s.raw = new_val.into();
}
None => {
let v = Vec::new();
self.references = Some(References {
raw: new_val,
raw: new_val.into(),
refs: v,
});
}

View File

@ -28,6 +28,45 @@ use nom::{ErrorKind, IResult, Needed};
use std;
use std::str::from_utf8;
pub trait BytesExt {
fn trim(&self) -> &Self;
fn find(&self, needle: &[u8]) -> Option<usize>;
fn replace(&self, from: &[u8], to: &[u8]) -> Vec<u8>;
}
impl BytesExt for [u8] {
fn trim(&self) -> &[u8] {
fn is_whitespace(c: &u8) -> bool {
*c == b'\t' || *c == b' '
}
fn is_not_whitespace(c: &u8) -> bool {
!is_whitespace(c)
}
if let Some(first) = self.iter().position(is_not_whitespace) {
if let Some(last) = self.iter().rposition(is_not_whitespace) {
&self[first..last + 1]
} else {
unreachable!();
}
} else {
&[]
}
}
// https://stackoverflow.com/a/35907071
fn find(&self, needle: &[u8]) -> Option<usize> {
self.windows(needle.len()).position(|window| window == needle)
}
fn replace(&self, from: &[u8], to: &[u8]) -> Vec<u8> {
let mut ret = self.to_vec();
if let Some(idx) = self.find(from) {
ret.splice(idx..(idx + from.len()), to.iter().cloned());
}
ret
}
}
macro_rules! is_whitespace {
($var:ident) => {
$var == b' ' && $var == b'\t' && $var == b'\n' && $var == b'\r'
@ -71,7 +110,7 @@ fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8], u8> {
* Tue, 5 Jan 2016 21:30:44 +0100 (CET)
*/
fn header_value(input: &[u8]) -> IResult<&[u8], &str> {
fn header_value(input: &[u8]) -> IResult<&[u8], &[u8]> {
if input.is_empty() || input[0] == b'\n' {
IResult::Incomplete(Needed::Unknown)
} else {
@ -79,15 +118,9 @@ fn header_value(input: &[u8]) -> IResult<&[u8], &str> {
for (i, x) in input.iter().enumerate() {
if *x == b'\n' {
if (i + 1) < input_len && input[i + 1] != b' ' && input[i + 1] != b'\t' {
return match from_utf8(&input[0..i]) {
Ok(v) => IResult::Done(&input[(i + 1)..], v),
Err(_) => IResult::Error(error_code!(ErrorKind::Custom(43))),
};
return IResult::Done(&input[(i + 1)..], &input[0..i]);
} else if i + 1 == input_len {
return match from_utf8(input) {
Ok(v) => IResult::Done(&input[(i + 1)..], v),
Err(_) => IResult::Error(error_code!(ErrorKind::Custom(43))),
};
return IResult::Done(&input[(i + 1)..], &input[0..i]);
}
}
}
@ -96,15 +129,15 @@ fn header_value(input: &[u8]) -> IResult<&[u8], &str> {
}
/* Parse the name part of the header -> &str */
named!(name<&str>, map_res!(is_not!(":\n"), from_utf8));
named!(name<&[u8]>, is_not!(":\n"));
/* Parse a single header as a tuple -> (&str, Vec<&str>) */
named!(
header<(&str, &str)>,
separated_pair!(complete!(name), ws!(tag!(":")), complete!(header_value))
header<(&[u8], &[u8])>,
separated_pair!(complete!(name), ws!(tag!(b":")), complete!(header_value))
);
/* Parse all headers -> Vec<(&str, Vec<&str>)> */
named!(pub headers<std::vec::Vec<(&str, &str)>>,
named!(pub headers<std::vec::Vec<(&[u8], &[u8])>>,
many1!(complete!(header)));
//named!(pub headers_raw<&[u8]>,
@ -128,9 +161,9 @@ named!(pub body_raw<&[u8]>,
body: take_while!(call!(|_| true)) >>
( { body } )));
named!(pub mail<(std::vec::Vec<(&str, &str)>, &[u8])>,
separated_pair!(headers, tag!("\n"), take_while!(call!(|_| true))));
named!(pub attachment<(std::vec::Vec<(&str, &str)>, &[u8])>,
named!(pub mail<(std::vec::Vec<(&[u8], &[u8])>, &[u8])>,
separated_pair!(headers, tag!(b"\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))) >>
@ -252,7 +285,7 @@ named!(
);
named!(
encoded_word_list<String>,
encoded_word_list<Vec<u8>>,
ws!(do_parse!(
list: separated_nonempty_list!(complete!(is_a!(" \n\r\t")), encoded_word) >> ({
let list_len = list.iter().fold(0, |mut acc, x| {
@ -264,17 +297,17 @@ named!(
acc.append(&mut x.clone());
acc
});
String::from_utf8_lossy(&bytes).into_owned()
bytes
})
))
);
named!(
ascii_token<String>,
ascii_token<Vec<u8>>,
do_parse!(
word: alt!(
terminated!(take_until1!("=?"), peek!(tag_no_case!("=?UTF-8?")))
| take_while!(call!(|_| true))
) >> ({ String::from_utf8_lossy(word).into_owned() })
) >> ({ word.into() })
)
);
@ -317,7 +350,7 @@ fn display_addr(input: &[u8]) -> IResult<&[u8], Address> {
IResult::Error(e) => IResult::Error(e),
IResult::Incomplete(i) => IResult::Incomplete(i),
IResult::Done(rest, raw) => {
display_name.length = raw.find('<').unwrap();
display_name.length = raw.find(b"<").unwrap();
address_spec.offset = display_name.length + 1;
address_spec.length = raw.len() - display_name.length - 2;
IResult::Done(
@ -357,7 +390,7 @@ fn addr_spec(input: &[u8]) -> IResult<&[u8], Address> {
IResult::Done(
&input[end..],
Address::Mailbox(MailboxAddress {
raw: String::from_utf8_lossy(&input[0..end + 1]).to_string(),
raw: input[0..end + 1].into(),
display_name: StrBuilder {
offset: 0,
length: 0,
@ -446,7 +479,7 @@ fn group(input: &[u8]) -> IResult<&[u8], Address> {
return IResult::Done(
rest,
Address::Group(GroupAddress {
raw: String::from_utf8(input[0..size].to_vec()).unwrap(),
raw: input[0..size].into(),
display_name: StrBuilder {
offset: 0,
length: dlength,
@ -480,13 +513,13 @@ named!(pub rfc2822address_list<Vec<Address>>, ws!(
named!(pub address_list<String>, ws!(do_parse!(
list: alt_complete!( encoded_word_list | ascii_token) >>
( {
let list: Vec<&str> = list.split(',').collect();
let list: Vec<&[u8]> = list.split(|c| *c == b',').collect();
let string_len = list.iter().fold(0, |mut acc, x| { acc+=x.trim().len(); acc }) + list.len() - 1;
let list_len = list.len();
let mut i = 0;
list.iter().fold(String::with_capacity(string_len),
|acc, x| {
let mut acc = acc + &x.replace("\n", "").replace("\t", " ").trim();
let mut acc = acc + &String::from_utf8_lossy(x.replace(b"\n", b"").replace(b"\t", b" ").trim());
if i != list_len - 1 {
acc.push_str(" ");
i+=1;
@ -497,20 +530,20 @@ named!(pub address_list<String>, ws!(do_parse!(
)));
named!(pub phrase<String>, ws!(do_parse!(
named!(pub phrase<Vec<u8>>, ws!(do_parse!(
list: many0!(alt_complete!( encoded_word_list | ascii_token)) >>
( {
if list.len() == 0 {
String::new()
Vec::new()
} else {
let string_len = list.iter().fold(0, |mut acc, x| { acc+=x.len(); acc }) + list.len() - 1;
let list_len = list.len();
let mut i = 0;
list.iter().fold(String::with_capacity(string_len),
|acc, x| {
let mut acc = acc + &x.replace("\n", "").replace("\t", " ");
list.iter().fold(Vec::with_capacity(string_len),
|mut acc, x| {
acc.extend(x.replace(b"\n", b"").replace(b"\t", b" "));
if i != list_len - 1 {
acc.push_str(" ");
acc.push(b' ');
i+=1;
}
acc
@ -578,21 +611,21 @@ fn test_phrase() {
phrase(phrase_s).unwrap()
);
}
fn eat_comments(input: &str) -> String {
fn eat_comments(input: &[u8]) -> Vec<u8> {
let mut in_comment = false;
input
.chars()
.fold(String::with_capacity(input.len()), |mut acc, x| {
if x == '(' && !in_comment {
.iter()
.fold(Vec::with_capacity(input.len()), |mut acc, x| {
if *x == b'(' && !in_comment {
in_comment = true;
acc
} else if x == ')' && in_comment {
} else if *x == b')' && in_comment {
in_comment = false;
acc
} else if in_comment {
acc
} else {
acc.push(x);
acc.push(*x);
acc
}
})
@ -610,12 +643,12 @@ fn test_eat_comments() {
* right now we expect input will have no extra spaces in between tokens
*
* We should use a custom parser here*/
pub fn date(input: &str) -> Option<chrono::DateTime<chrono::FixedOffset>> {
let parsed_result = phrase(eat_comments(input).as_bytes())
pub fn date(input: &[u8]) -> Option<chrono::DateTime<chrono::FixedOffset>> {
let parsed_result = phrase(&eat_comments(input))
.to_full_result()
.unwrap()
.replace("-", "+");
chrono::DateTime::parse_from_rfc2822(parsed_result.trim()).ok()
.replace(b"-",b"+");
chrono::DateTime::parse_from_rfc2822(String::from_utf8_lossy(parsed_result.trim()).as_ref()).ok()
}
#[test]
@ -627,11 +660,11 @@ fn test_date() {
assert_eq!(date(_s).unwrap(), date(__s).unwrap());
}
named!(pub message_id<&str>,
map_res!(complete!(delimited!(tag!("<"), take_until1!(">"), tag!(">"))), from_utf8)
named!(pub message_id<&[u8]>,
complete!(delimited!(tag!("<"), take_until1!(">"), tag!(">")))
);
fn message_id_peek(input: &[u8]) -> IResult<&[u8], &str> {
fn message_id_peek(input: &[u8]) -> IResult<&[u8], &[u8]> {
let input_length = input.len();
if input.is_empty() {
IResult::Incomplete(Needed::Size(1))
@ -640,16 +673,16 @@ fn message_id_peek(input: &[u8]) -> IResult<&[u8], &str> {
} else {
for (i, &x) in input.iter().take(input_length).enumerate().skip(1) {
if x == b'>' {
return IResult::Done(&input[i + 1..], from_utf8(&input[0..i + 1]).unwrap());
return IResult::Done(&input[i + 1..], &input[0..i + 1]);
}
}
IResult::Incomplete(Needed::Unknown)
}
}
named!(pub references<Vec<&str>>, separated_list!(complete!(is_a!(" \n\t\r")), message_id_peek));
named!(pub references<Vec<&[u8]>>, separated_list!(complete!(is_a!(" \n\t\r")), message_id_peek));
named_args!(pub attachments<'a>(boundary: &'a str, boundary_end: &'a str) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >,
named_args!(pub attachments<'a>(boundary: &'a [u8], boundary_end: &'a [u8]) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >,
alt_complete!(do_parse!(
take_until!(boundary) >>
vecs: many0!(complete!(do_parse!(
@ -688,31 +721,27 @@ fn test_attachments() {
}
named!(
content_type_parameter<(&str, &str)>,
content_type_parameter<(&[u8], &[u8])>,
do_parse!(
tag!(";") >> name: terminated!(map_res!(ws!(take_until!("=")), from_utf8), tag!("="))
>> value:
map_res!(
ws!(alt_complete!(
delimited!(tag!("\""), take_until!("\""), tag!("\"")) | is_not!(";")
)),
from_utf8
) >> ({ (name, value) })
tag!(";") >>
name: terminated!(ws!(take_until!("=")) , tag!("=")) >>
value: ws!(alt_complete!( delimited!(tag!("\""), take_until!("\""), tag!("\"")) | is_not!(";"))) >>
({ (name, value) })
)
);
named!(pub content_type< (&str, &str, Vec<(&str, &str)>) >,
named!(pub content_type< (&[u8], &[u8], Vec<(&[u8], &[u8])>) >,
do_parse!(
_type: map_res!(take_until!("/"), from_utf8) >>
_type: take_until!("/") >>
tag!("/") >>
_subtype: map_res!(is_not!(";"), from_utf8) >>
_subtype: is_not!(";") >>
parameters: many0!(complete!(content_type_parameter)) >>
( {
(_type, _subtype, parameters)
} )
));
named!(pub quoted_printable_text<String>,
named!(pub quoted_printable_text<Vec<u8>>,
do_parse!(
bytes: many0!(alt_complete!(
preceded!(tag!("=\n"), quoted_printable_byte) |
@ -720,7 +749,7 @@ named!(pub quoted_printable_text<String>,
quoted_printable_byte |
le_u8)) >>
( {
String::from_utf8_lossy(&bytes).into_owned()
bytes
} )
)
);

View File

@ -25,7 +25,7 @@ use mailbox::Mailbox;
extern crate fnv;
use self::fnv::FnvHashMap;
use std;
use std::borrow::Cow;
type UnixTimestamp = u64;
@ -115,12 +115,13 @@ impl PartialEq for Container {
fn build_collection(
threads: &mut Vec<Container>,
id_table: &mut FnvHashMap<std::string::String, usize>,
id_table: &mut FnvHashMap<Cow<str>, usize>,
collection: &mut [Envelope],
) -> () {
for (i, x) in collection.iter_mut().enumerate() {
let x_index; /* x's index in threads */
let m_id = x.message_id_raw().to_string();
let m_id = x.message_id_raw().into_owned();
let m_id = Cow::from(m_id);
/* TODO: Check for missing Message-ID.
* Solutions: generate a hidden one
*/
@ -170,8 +171,9 @@ fn build_collection(
continue;
}
iasf += 1;
let parent_id = if id_table.contains_key(r.raw()) {
let p = id_table[r.raw()];
let r = String::from_utf8_lossy(r.raw());
let parent_id = if id_table.contains_key(&r) {
let p = id_table[r.as_ref()];
if !(threads[p].is_descendant(threads, &threads[curr_ref])
|| threads[curr_ref].is_descendant(threads, &threads[p]))
{
@ -204,7 +206,8 @@ fn build_collection(
if threads[curr_ref].parent.is_none() {
threads[curr_ref].parent = Some(idx);
}
id_table.insert(r.raw().to_string(), idx);
/* Can't avoid copy here since we have different lifetimes */
id_table.insert(Cow::from(r.into_owned()), idx);
idx
};
/* update thread date */
@ -238,7 +241,7 @@ pub fn build_threads(
/* a vector to hold thread members */
let mut threads: Vec<Container> = Vec::with_capacity((collection.len() as f64 * 1.2) as usize);
/* A hash table of Message IDs */
let mut id_table: FnvHashMap<std::string::String, usize> =
let mut id_table: FnvHashMap<Cow<str>, usize> =
FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default());
/* Add each message to id_table and threads, and link them together according to the
@ -261,13 +264,14 @@ pub fn build_threads(
let sent_mailbox = sent_mailbox.unwrap();
for x in &sent_mailbox.collection {
if id_table.contains_key(x.message_id_raw())
let m_id = x.message_id_raw();
if id_table.contains_key(&m_id)
|| (!x.in_reply_to_raw().is_empty()
&& id_table.contains_key(x.in_reply_to_raw()))
&& id_table.contains_key(&x.in_reply_to_raw()))
{
let mut x: Envelope = (*x).clone();
if id_table.contains_key(x.message_id_raw()) {
let c = id_table[x.message_id_raw()];
if id_table.contains_key(&m_id) {
let c = id_table[&m_id];
if threads[c].message.is_some() {
/* skip duplicate message-id, but this should be handled instead */
continue;
@ -277,11 +281,11 @@ pub fn build_threads(
threads[c].date = x.date();
x.set_thread(c);
} else if !x.in_reply_to_raw().is_empty()
&& id_table.contains_key(x.in_reply_to_raw())
&& id_table.contains_key(&x.in_reply_to_raw())
{
let p = id_table[x.in_reply_to_raw()];
let c = if id_table.contains_key(x.message_id_raw()) {
id_table[x.message_id_raw()]
let p = id_table[&m_id];
let c = if id_table.contains_key(&m_id) {
id_table[&m_id]
} else {
threads.push(Container {
message: Some(idx),
@ -293,7 +297,7 @@ pub fn build_threads(
indentation: 0,
show_subject: true,
});
id_table.insert(x.message_id_raw().to_string(), tidx);
id_table.insert(Cow::from(m_id.into_owned()), tidx);
x.set_thread(tidx);
tidx += 1;
tidx - 1
@ -376,7 +380,7 @@ pub fn build_threads(
if indentation > 0 && thread.has_message() {
let subject = collection[thread.message().unwrap()].subject();
if subject == root_subject
|| subject.starts_with("Re: ") && subject.ends_with(root_subject)
|| subject.starts_with("Re: ") && subject.as_ref().ends_with(root_subject.as_ref())
{
threads[i].set_show_subject(false);
}

View File

@ -421,7 +421,7 @@ impl Component for StatusBar {
.as_ref()
.unwrap();
self.status = format!(
"{} |Mailbox: {}, Messages: {}, New: {}",
"{} | Mailbox: {}, Messages: {}, New: {}",
self.mode,
m.folder.name(),
m.collection.len(),