Remove some string conversions in parsing
parent
d5c0542f61
commit
e91f22cb4f
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
} )
|
||||
)
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Reference in New Issue