melib: update nom dependency from 3.2.0 to 5.1.1

That was hecking exhausting
memfd
Manos Pitsidianakis 2020-06-06 19:38:20 +03:00
parent db4c401828
commit 6ec249dd7f
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
24 changed files with 1752 additions and 1257 deletions

75
Cargo.lock generated
View File

@ -12,6 +12,15 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arrayvec"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
dependencies = [
"nodrop",
]
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.5.1" version = "0.5.1"
@ -59,7 +68,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
dependencies = [ dependencies = [
"arrayref", "arrayref",
"arrayvec", "arrayvec 0.5.1",
"constant_time_eq", "constant_time_eq",
] ]
@ -639,6 +648,19 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
[[package]]
name = "lexical-core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7043aa5c05dd34fb73b47acb8c3708eac428de4545ea3682ed2f11293ebd890"
dependencies = [
"arrayvec 0.4.12",
"cfg-if",
"rustc_version",
"ryu",
"static_assertions",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.71" version = "0.2.71"
@ -750,7 +772,7 @@ dependencies = [
"linkify", "linkify",
"melib", "melib",
"nix", "nix",
"nom", "nom 3.2.1",
"notify", "notify",
"notify-rust", "notify-rust",
"pcre2", "pcre2",
@ -785,7 +807,7 @@ dependencies = [
"memmap", "memmap",
"native-tls", "native-tls",
"nix", "nix",
"nom", "nom 5.1.1",
"notify", "notify",
"notify-rust", "notify-rust",
"reqwest", "reqwest",
@ -937,6 +959,12 @@ dependencies = [
"void", "void",
] ]
[[package]]
name = "nodrop"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]] [[package]]
name = "nom" name = "nom"
version = "3.2.1" version = "3.2.1"
@ -946,6 +974,17 @@ dependencies = [
"memchr 1.0.2", "memchr 1.0.2",
] ]
[[package]]
name = "nom"
version = "5.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6"
dependencies = [
"lexical-core",
"memchr 2.3.3",
"version_check",
]
[[package]] [[package]]
name = "notify" name = "notify"
version = "4.0.15" version = "4.0.15"
@ -1339,6 +1378,15 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.4" version = "1.0.4"
@ -1393,6 +1441,21 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.110" version = "1.0.110"
@ -1480,6 +1543,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "static_assertions"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.27" version = "1.0.27"

View File

@ -23,7 +23,8 @@ crossbeam = "0.7.2"
data-encoding = "2.1.1" data-encoding = "2.1.1"
encoding = "0.2.33" encoding = "0.2.33"
memmap = { version = "0.5.2", optional = true } memmap = { version = "0.5.2", optional = true }
nom = "3.2.0" nom = { version = "5.1.1" }
notify = { version = "4.0.1", optional = true } notify = { version = "4.0.1", optional = true }
notify-rust = { version = "^3", optional = true } notify-rust = { version = "^3", optional = true }
termion = "1.5.1" termion = "1.5.1"

View File

@ -533,11 +533,11 @@ impl MailBackend for ImapType {
Ok(()) Ok(())
} }
fn as_any(&self) -> &dyn ::std::any::Any { fn as_any(&self) -> &dyn::std::any::Any {
self self
} }
fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any { fn as_any_mut(&mut self) -> &mut dyn::std::any::Any {
self self
} }
@ -967,7 +967,7 @@ impl ImapType {
lines.next_back(); lines.next_back();
for l in lines.map(|l| l.trim()) { for l in lines.map(|l| l.trim()) {
if let Ok(mut mailbox) = if let Ok(mut mailbox) =
protocol_parser::list_mailbox_result(l.as_bytes()).to_full_result() protocol_parser::list_mailbox_result(l.as_bytes()).map(|(_, v)| v)
{ {
if let Some(parent) = mailbox.parent { if let Some(parent) = mailbox.parent {
if mailboxes.contains_key(&parent) { if mailboxes.contains_key(&parent) {
@ -1007,7 +1007,7 @@ impl ImapType {
lines.next_back(); lines.next_back();
for l in lines.map(|l| l.trim()) { for l in lines.map(|l| l.trim()) {
if let Ok(subscription) = if let Ok(subscription) =
protocol_parser::list_mailbox_result(l.as_bytes()).to_full_result() protocol_parser::list_mailbox_result(l.as_bytes()).map(|(_, v)| v)
{ {
if let Some(f) = mailboxes.get_mut(&subscription.hash()) { if let Some(f) = mailboxes.get_mut(&subscription.hash()) {
if subscription.no_select { if subscription.no_select {

View File

@ -195,8 +195,8 @@ impl ImapStream {
.ok_or_else(|| MeliError::new("")) .ok_or_else(|| MeliError::new(""))
.and_then(|res| { .and_then(|res| {
protocol_parser::capabilities(res.as_bytes()) protocol_parser::capabilities(res.as_bytes())
.to_full_result()
.map_err(|_| MeliError::new("")) .map_err(|_| MeliError::new(""))
.map(|(_, v)| v)
}); });
if capabilities.is_err() { if capabilities.is_err() {
@ -241,8 +241,7 @@ impl ImapStream {
for l in res.split_rn() { for l in res.split_rn() {
if l.starts_with("* CAPABILITY") { if l.starts_with("* CAPABILITY") {
capabilities = protocol_parser::capabilities(l.as_bytes()) capabilities = protocol_parser::capabilities(l.as_bytes())
.to_full_result() .map(|(_, capabilities)| {
.map(|capabilities| {
HashSet::from_iter(capabilities.into_iter().map(|s: &[u8]| s.to_vec())) HashSet::from_iter(capabilities.into_iter().map(|s: &[u8]| s.to_vec()))
}) })
.ok(); .ok();
@ -269,7 +268,7 @@ impl ImapStream {
drop(capabilities); drop(capabilities);
ret.send_command(b"CAPABILITY")?; ret.send_command(b"CAPABILITY")?;
ret.read_response(&mut res).unwrap(); ret.read_response(&mut res).unwrap();
let capabilities = protocol_parser::capabilities(res.as_bytes()).to_full_result()?; let capabilities = protocol_parser::capabilities(res.as_bytes())?.1;
let capabilities = HashSet::from_iter(capabilities.into_iter().map(|s| s.to_vec())); let capabilities = HashSet::from_iter(capabilities.into_iter().map(|s| s.to_vec()));
Ok((capabilities, ret)) Ok((capabilities, ret))
} else { } else {

View File

@ -23,48 +23,53 @@ use super::{ImapConnection, ImapProtocol, ImapServerConf, UIDStore};
use crate::conf::AccountSettings; use crate::conf::AccountSettings;
use crate::error::{MeliError, Result}; use crate::error::{MeliError, Result};
use crate::get_conf_val; use crate::get_conf_val;
use nom::IResult; use nom::{
branch::alt, bytes::complete::tag, combinator::map, error::ErrorKind,
multi::separated_nonempty_list, sequence::separated_pair, IResult,
};
use std::str::FromStr; use std::str::FromStr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Instant; use std::time::Instant;
named!( pub fn managesieve_capabilities(input: &[u8]) -> Result<Vec<(&[u8], &[u8])>> {
pub managesieve_capabilities<Vec<(&[u8], &[u8])>>, let (_, ret) = separated_nonempty_list(
do_parse!( tag(b"\r\n"),
ret: separated_nonempty_list_complete!(tag!(b"\r\n"), alt_complete!(separated_pair!(quoted_raw, tag!(b" "), quoted_raw) | map!(quoted_raw, |q| (q, &b""[..])))) alt((
>> opt!(tag!("\r\n")) separated_pair(quoted_raw, tag(b" "), quoted_raw),
>> ({ ret }) map(quoted_raw, |q| (q, &b""[..])),
) )),
); )(input)?;
Ok(ret)
}
#[test] #[test]
fn test_managesieve_capabilities() { fn test_managesieve_capabilities() {
assert_eq!(managesieve_capabilities(b"\"IMPLEMENTATION\" \"Dovecot Pigeonhole\"\r\n\"SIEVE\" \"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext\"\r\n\"NOTIFY\" \"mailto\"\r\n\"SASL\" \"PLAIN\"\r\n\"STARTTLS\"\r\n\"VERSION\" \"1.0\"\r\n").to_full_result(), Ok(vec![ assert_eq!(managesieve_capabilities(b"\"IMPLEMENTATION\" \"Dovecot Pigeonhole\"\r\n\"SIEVE\" \"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext\"\r\n\"NOTIFY\" \"mailto\"\r\n\"SASL\" \"PLAIN\"\r\n\"STARTTLS\"\r\n\"VERSION\" \"1.0\"\r\n").unwrap(), vec![
(&b"IMPLEMENTATION"[..],&b"Dovecot Pigeonhole"[..]), (&b"IMPLEMENTATION"[..],&b"Dovecot Pigeonhole"[..]),
(&b"SIEVE"[..],&b"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext"[..]), (&b"SIEVE"[..],&b"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext"[..]),
(&b"NOTIFY"[..],&b"mailto"[..]), (&b"NOTIFY"[..],&b"mailto"[..]),
(&b"SASL"[..],&b"PLAIN"[..]), (&b"SASL"[..],&b"PLAIN"[..]),
(&b"STARTTLS"[..], &b""[..]), (&b"STARTTLS"[..], &b""[..]),
(&b"VERSION"[..],&b"1.0"[..])]) (&b"VERSION"[..],&b"1.0"[..])]
); );
} }
// Return a byte sequence surrounded by "s and decoded if necessary // Return a byte sequence surrounded by "s and decoded if necessary
pub fn quoted_raw(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn quoted_raw(input: &[u8]) -> IResult<&[u8], &[u8]> {
if input.is_empty() || input[0] != b'"' { if input.is_empty() || input[0] != b'"' {
return IResult::Error(nom::ErrorKind::Custom(0)); return Err(nom::Err::Error((input, ErrorKind::Tag)));
} }
let mut i = 1; let mut i = 1;
while i < input.len() { while i < input.len() {
if input[i] == b'\"' && input[i - 1] != b'\\' { if input[i] == b'\"' && input[i - 1] != b'\\' {
return IResult::Done(&input[i + 1..], &input[1..i]); return Ok((&input[i + 1..], &input[1..i]));
} }
i += 1; i += 1;
} }
return IResult::Error(nom::ErrorKind::Custom(0)); Err(nom::Err::Error((input, ErrorKind::Tag)))
} }
pub trait ManageSieve { pub trait ManageSieve {

View File

@ -142,7 +142,7 @@ impl BackendOp for ImapOp {
response.lines().collect::<Vec<&str>>().len() response.lines().collect::<Vec<&str>>().len()
); );
match protocol_parser::uid_fetch_flags_response(response.as_bytes()) match protocol_parser::uid_fetch_flags_response(response.as_bytes())
.to_full_result() .map(|(_, v)| v)
.map_err(MeliError::from) .map_err(MeliError::from)
{ {
Ok(v) => { Ok(v) => {
@ -181,7 +181,7 @@ impl BackendOp for ImapOp {
conn.read_response(&mut response, RequiredResponses::STORE_REQUIRED)?; conn.read_response(&mut response, RequiredResponses::STORE_REQUIRED)?;
debug!(&response); debug!(&response);
match protocol_parser::uid_fetch_flags_response(response.as_bytes()) match protocol_parser::uid_fetch_flags_response(response.as_bytes())
.to_full_result() .map(|(_, v)| v)
.map_err(MeliError::from) .map_err(MeliError::from)
{ {
Ok(v) => { Ok(v) => {
@ -215,7 +215,7 @@ impl BackendOp for ImapOp {
)?; )?;
conn.read_response(&mut response, RequiredResponses::STORE_REQUIRED)?; conn.read_response(&mut response, RequiredResponses::STORE_REQUIRED)?;
protocol_parser::uid_fetch_flags_response(response.as_bytes()) protocol_parser::uid_fetch_flags_response(response.as_bytes())
.to_full_result() .map(|(_, v)| v)
.map_err(MeliError::from)?; .map_err(MeliError::from)?;
let hash = tag_hash!(tag); let hash = tag_hash!(tag);
if value { if value {

View File

@ -22,7 +22,16 @@
use super::*; use super::*;
use crate::email::parser::BytesExt; use crate::email::parser::BytesExt;
use crate::get_path_hash; use crate::get_path_hash;
use nom::{digit, is_digit, rest, IResult}; use nom::{
branch::{alt, permutation},
bytes::complete::{is_a, is_not, tag, take, take_until, take_while},
character::complete::digit1,
character::is_digit,
combinator::{map, map_res, opt, rest},
multi::{length_data, many0, many1, separated_list, separated_nonempty_list},
sequence::{delimited, preceded},
IResult,
};
use std::str::FromStr; use std::str::FromStr;
bitflags! { bitflags! {
@ -277,7 +286,7 @@ macro_rules! to_str (
($v:expr) => (unsafe{ std::str::from_utf8_unchecked($v) }) ($v:expr) => (unsafe{ std::str::from_utf8_unchecked($v) })
); );
macro_rules! dbg_dmp ( /*macro_rules! dbg_dmp (
($i: expr, $submac:ident!( $($args:tt)* )) => ( ($i: expr, $submac:ident!( $($args:tt)* )) => (
{ {
let l = line!(); let l = line!();
@ -299,13 +308,59 @@ macro_rules! dbg_dmp (
dbg_dmp!($i, call!($f)); dbg_dmp!($i, call!($f));
); );
); );
*/
/* /*
* LIST (\HasNoChildren) "." INBOX.Sent * LIST (\HasNoChildren) "." INBOX.Sent
* LIST (\HasChildren) "." INBOX * LIST (\HasChildren) "." INBOX
*/ */
named!(
pub list_mailbox_result<ImapMailbox>, pub fn list_mailbox_result(input: &[u8]) -> IResult<&[u8], ImapMailbox> {
let (input, _) = alt((tag("* LIST ("), tag("* LSUB (")))(input.ltrim())?;
let (input, properties) = take_until(&b")"[0..])(input)?;
let (input, _) = tag(b") ")(input)?;
let (input, separator) = delimited(tag(b"\""), take(1_u32), tag(b"\""))(input)?;
let (input, _) = take(1_u32)(input)?;
let (input, path) = alt((delimited(tag("\""), is_not("\""), tag("\"")), rest))(input)?;
Ok((
input,
({
let separator: u8 = separator[0];
let mut f = ImapMailbox::default();
f.no_select = false;
f.is_subscribed = false;
for p in properties.split(|&b| b == b' ') {
use crate::backends::SpecialUsageMailbox;
if p.eq_ignore_ascii_case(b"\\NoSelect") {
f.no_select = true;
} else if p.eq_ignore_ascii_case(b"\\Sent") {
let _ = f.set_special_usage(SpecialUsageMailbox::Sent);
} else if p.eq_ignore_ascii_case(b"\\Junk") {
let _ = f.set_special_usage(SpecialUsageMailbox::Trash);
} else if p.eq_ignore_ascii_case(b"\\Drafts") {
let _ = f.set_special_usage(SpecialUsageMailbox::Drafts);
}
}
f.imap_path = String::from_utf8_lossy(path).into();
f.hash = get_path_hash!(&f.imap_path);
f.path = if separator == b'/' {
f.imap_path.clone()
} else {
f.imap_path.replace(separator as char, "/")
};
f.name = if let Some(pos) = f.imap_path.as_bytes().iter().rposition(|&c| c == separator)
{
f.parent = Some(get_path_hash!(&f.imap_path[..pos]));
f.imap_path[pos + 1..].to_string()
} else {
f.imap_path.clone()
};
f.separator = separator;
debug!(f)
}),
))
/*
do_parse!( do_parse!(
ws!(alt_complete!(tag!("* LIST (") | tag!("* LSUB ("))) ws!(alt_complete!(tag!("* LIST (") | tag!("* LSUB (")))
>> properties: take_until!(&b")"[0..]) >> properties: take_until!(&b")"[0..])
@ -348,42 +403,74 @@ named!(
debug!(f) debug!(f)
}) })
) )
); */
}
named!( pub fn my_flags(input: &[u8]) -> IResult<&[u8], Flag> {
my_flags<Flag>, let (input, flags) = separated_list(tag(" "), preceded(tag("\\"), is_not(")")))(input)?;
do_parse!( let mut ret = Flag::default();
flags: separated_list!(tag!(" "), preceded!(tag!("\\"), is_not!(")"))) for f in flags {
>> ({ match f {
let mut ret = Flag::default(); b"Answered" => {
for f in flags { ret.set(Flag::REPLIED, true);
match f { }
b"Answered" => { b"Flagged" => {
ret.set(Flag::REPLIED, true); ret.set(Flag::FLAGGED, true);
} }
b"Flagged" => { b"Deleted" => {
ret.set(Flag::FLAGGED, true); ret.set(Flag::TRASHED, true);
} }
b"Deleted" => { b"Seen" => {
ret.set(Flag::TRASHED, true); ret.set(Flag::SEEN, true);
} }
b"Seen" => { b"Draft" => {
ret.set(Flag::SEEN, true); ret.set(Flag::DRAFT, true);
} }
b"Draft" => { f => {
ret.set(Flag::DRAFT, true); debug!("unknown Flag token value: {}", unsafe {
} std::str::from_utf8_unchecked(f)
f => { });
debug!("unknown Flag token value: {}", unsafe { }
std::str::from_utf8_unchecked(f) }
}); }
Ok((input, ret))
/*
my_flags<Flag>,
do_parse!(
flags: separated_list!(tag!(" "), preceded!(tag!("\\"), is_not!(")")))
>> ({
let mut ret = Flag::default();
for f in flags {
match f {
b"Answered" => {
ret.set(Flag::REPLIED, true);
}
b"Flagged" => {
ret.set(Flag::FLAGGED, true);
}
b"Deleted" => {
ret.set(Flag::TRASHED, true);
}
b"Seen" => {
ret.set(Flag::SEEN, true);
}
b"Draft" => {
ret.set(Flag::DRAFT, true);
}
f => {
debug!("unknown Flag token value: {}", unsafe {
std::str::from_utf8_unchecked(f)
});
}
} }
} }
} ret
ret })
}) )
) */
); }
#[derive(Debug)] #[derive(Debug)]
pub struct UidFetchResponse<'a> { pub struct UidFetchResponse<'a> {
@ -459,7 +546,10 @@ pub fn uid_fetch_response(input: &str) -> ImapParseResult<UidFetchResponse<'_>>
if input[i..].starts_with("UID ") { if input[i..].starts_with("UID ") {
i += "UID ".len(); i += "UID ".len();
if let IResult::Done(rest, uid) = take_while!(input[i..].as_bytes(), call!(is_digit)) { if let Ok((rest, uid)) = take_while::<_, &[u8], (&[u8], nom::error::ErrorKind)>(
is_digit,
)(input[i..].as_bytes())
{
i += input.len() - i - rest.len(); i += input.len() - i - rest.len();
ret.uid = usize::from_str(unsafe { std::str::from_utf8_unchecked(uid) }).unwrap(); ret.uid = usize::from_str(unsafe { std::str::from_utf8_unchecked(uid) }).unwrap();
} else { } else {
@ -470,7 +560,7 @@ pub fn uid_fetch_response(input: &str) -> ImapParseResult<UidFetchResponse<'_>>
} }
} else if input[i..].starts_with("FLAGS (") { } else if input[i..].starts_with("FLAGS (") {
i += "FLAGS (".len(); i += "FLAGS (".len();
if let IResult::Done(rest, flags) = flags(&input[i..]) { if let Ok((rest, flags)) = flags(&input[i..]) {
ret.flags = Some(flags); ret.flags = Some(flags);
i += (input.len() - i - rest.len()) + 1; i += (input.len() - i - rest.len()) + 1;
} else { } else {
@ -481,16 +571,15 @@ pub fn uid_fetch_response(input: &str) -> ImapParseResult<UidFetchResponse<'_>>
} }
} else if input[i..].starts_with("RFC822 {") { } else if input[i..].starts_with("RFC822 {") {
i += "RFC822 ".len(); i += "RFC822 ".len();
if let IResult::Done(rest, body) = length_bytes!( if let Ok((rest, body)) =
input[i..].as_bytes(), length_data::<_, _, (&[u8], nom::error::ErrorKind), _>(delimited(
delimited!( tag("{"),
tag!("{"), map_res(digit1, |s| {
map_res!(digit, |s| {
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
}), }),
tag!("}\r\n") tag("}\r\n"),
) ))(input[i..].as_bytes())
) { {
ret.body = Some(body); ret.body = Some(body);
i += input.len() - i - rest.len(); i += input.len() - i - rest.len();
} else { } else {
@ -501,7 +590,7 @@ pub fn uid_fetch_response(input: &str) -> ImapParseResult<UidFetchResponse<'_>>
} }
} else if input[i..].starts_with("ENVELOPE (") { } else if input[i..].starts_with("ENVELOPE (") {
i += "ENVELOPE ".len(); i += "ENVELOPE ".len();
if let IResult::Done(rest, envelope) = envelope(input[i..].as_bytes()) { if let Ok((rest, envelope)) = envelope(input[i..].as_bytes()) {
ret.envelope = Some(envelope); ret.envelope = Some(envelope);
i += input.len() - i - rest.len(); i += input.len() - i - rest.len();
} else { } else {
@ -596,34 +685,64 @@ pub fn uid_fetch_responses(mut input: &str) -> ImapParseResult<Vec<UidFetchRespo
* *
* "* 1 FETCH (FLAGS (\Seen) UID 1 RFC822.HEADER {5224} " * "* 1 FETCH (FLAGS (\Seen) UID 1 RFC822.HEADER {5224} "
*/ */
named!( pub fn uid_fetch_response_(
pub uid_fetch_response_<Vec<(usize, Option<(Flag, Vec<String>)>, &[u8])>>, input: &[u8],
many0!( ) -> IResult<&[u8], Vec<(usize, Option<(Flag, Vec<String>)>, &[u8])>> {
do_parse!( many0(
tag!("* ") |input| -> IResult<&[u8], (usize, Option<(Flag, Vec<String>)>, &[u8])> {
>> take_while!(call!(is_digit)) let (input, _) = tag("* ")(input)?;
>> tag!(" FETCH (") let (input, _) = take_while(is_digit)(input)?;
>> result: permutation!(preceded!(ws!(tag!("UID ")), map_res!(digit, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) })), opt!(preceded!(ws!(tag!("FLAGS ")), delimited!(tag!("("), byte_flags, tag!(")")))), let (input, result) = permutation((
ws!(length_bytes!(delimited!(tag!("{"), map_res!(digit, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) }), tag!("}\r\n"))))) preceded(
>> tag!(")\r\n") tag("UID "),
>> ((result.0, result.1, result.2)) map_res(digit1, |s| {
) usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
) }),
); ),
opt(preceded(
tag("FLAGS "),
delimited(tag("("), byte_flags, tag(")")),
)),
length_data(delimited(
tag("{"),
map_res(digit1, |s| {
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
}),
tag("}\r\n"),
)),
))(input.ltrim())?;
let (input, _) = tag(")\r\n")(input)?;
Ok((input, (result.0, result.1, result.2)))
},
)(input)
}
named!( pub fn uid_fetch_flags_response(input: &[u8]) -> IResult<&[u8], Vec<(usize, (Flag, Vec<String>))>> {
pub uid_fetch_flags_response<Vec<(usize, (Flag, Vec<String>))>>, many0(|input| -> IResult<&[u8], (usize, (Flag, Vec<String>))> {
many0!( let (input, _) = tag("* ")(input)?;
do_parse!( let (input, _) = take_while(is_digit)(input)?;
tag!("* ") let (input, _) = tag(" FETCH ( ")(input)?;
>> take_while!(call!(is_digit)) let (input, uid_flags) = permutation((
>> tag!(" FETCH (") preceded(
>> uid_flags: permutation!(preceded!(ws!(tag!("UID ")), map_res!(digit, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) })), preceded!(ws!(tag!("FLAGS ")), delimited!(tag!("("), byte_flags, tag!(")")))) tag("UID "),
>> tag!(")\r\n") map_res(digit1, |s| {
>> ((uid_flags.0, uid_flags.1)) usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
}),
),
preceded(tag("FLAGS "), delimited(tag("("), byte_flags, tag(")"))),
))(input.ltrim())?;
let (input, _) = tag(")\r\n")(input)?;
Ok((input, (uid_flags.0, uid_flags.1)))
})(input)
/*
>> uid_flags: permutation!(preceded!(ws!(tag!("UID ")), map_res!(digit1, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) })), preceded!(ws!(tag!("FLAGS ")), delimited!(tag!("("), byte_flags, tag!(")"))))
>> tag!(")\r\n")
>> ((uid_flags.0, uid_flags.1))
)
) )
) */
); }
macro_rules! flags_to_imap_list { macro_rules! flags_to_imap_list {
($flags:ident) => {{ ($flags:ident) => {{
@ -667,8 +786,15 @@ macro_rules! flags_to_imap_list {
* "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE AUTH=PLAIN\r\n" * "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE AUTH=PLAIN\r\n"
*/ */
named!( pub fn capabilities(input: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
pub capabilities<Vec<&[u8]>>, let (input, _) = take_until("CAPABILITY ")(input)?;
let (input, _) = tag("CAPABILITY ")(input)?;
let (input, ret) = separated_nonempty_list(tag(" "), is_not(" ]\r\n"))(input)?;
let (input, _) = take_until("\r\n")(input)?;
let (input, _) = tag("\r\n")(input)?;
Ok((input, ret))
/*
pub capabilities<>,
do_parse!( do_parse!(
take_until!("CAPABILITY ") take_until!("CAPABILITY ")
>> tag!("CAPABILITY ") >> tag!("CAPABILITY ")
@ -677,7 +803,8 @@ named!(
>> tag!("\r\n") >> tag!("\r\n")
>> ({ ret }) >> ({ ret })
) )
); */
}
/// This enum represents the server's untagged responses detailed in `7. Server Responses` of RFC 3501 INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1 /// This enum represents the server's untagged responses detailed in `7. Server Responses` of RFC 3501 INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1
pub enum UntaggedResponse { pub enum UntaggedResponse {
@ -746,62 +873,130 @@ pub enum UntaggedResponse {
}, },
} }
named!( pub fn untagged_responses(input: &[u8]) -> IResult<&[u8], Option<UntaggedResponse>> {
pub untagged_responses<Option<UntaggedResponse>>, let (input, _) = tag("* ")(input)?;
do_parse!( let (input, num) = map_res(digit1, |s| {
tag!("* ") usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
>> num: map_res!(digit, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) }) })(input)?;
>> tag!(" ") let (input, _) = tag(" ")(input)?;
>> tag: take_until!("\r\n") let (input, _tag) = take_until("\r\n")(input)?;
>> tag!("\r\n") let (input, _) = tag("\r\n")(input)?;
>> ({ Ok((input, {
use UntaggedResponse::*; use UntaggedResponse::*;
match tag { match _tag {
b"EXPUNGE" => Some(Expunge(num)), b"EXPUNGE" => Some(Expunge(num)),
b"EXISTS" => Some(Exists(num)), b"EXISTS" => Some(Exists(num)),
b"RECENT" => Some(Recent(num)), b"RECENT" => Some(Recent(num)),
_ if tag.starts_with(b"FETCH ") => flags( _ if _tag.starts_with(b"FETCH ") => {
unsafe { std::str::from_utf8_unchecked(&tag[b"FETCH (FLAGS (".len()..]) }).map(|flags| Fetch(num, flags)).to_full_result().map_err(|err| debug!("untagged_response malformed fetch: {}", unsafe { std::str::from_utf8_unchecked(tag) })).ok(), flags(unsafe { std::str::from_utf8_unchecked(&_tag[b"FETCH (FLAGS (".len()..]) })
_ => { .map(|(_, flags)| Fetch(num, flags))
debug!("unknown untagged_response: {}", unsafe { std::str::from_utf8_unchecked(tag) }); .map_err(|err| {
None debug!(
} "untagged_response malformed fetch: {} {}",
unsafe { std::str::from_utf8_unchecked(_tag) },
err
)
})
.ok()
} }
}) _ => {
) debug!("unknown untagged_response: {}", unsafe {
); std::str::from_utf8_unchecked(_tag)
});
None
}
}
}))
/*
pub untagged_responses<>,
do_parse!(
tag!("* ")
>> num: map_res!(digit1, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) })
>> tag!(" ")
>> tag: take_until!("\r\n")
>> tag!("\r\n")
>> ({
use UntaggedResponse::*;
match tag {
b"EXPUNGE" => Some(Expunge(num)),
b"EXISTS" => Some(Exists(num)),
b"RECENT" => Some(Recent(num)),
_ if tag.starts_with(b"FETCH ") => flags(
unsafe { std::str::from_utf8_unchecked(&tag[b"FETCH (FLAGS (".len()..]) }).map(|flags| Fetch(num, flags)).map_err(|err| debug!("untagged_response malformed fetch: {}", unsafe { std::str::from_utf8_unchecked(tag) })).ok(),
_ => {
debug!("unknown untagged_response: {}", unsafe { std::str::from_utf8_unchecked(tag) });
None
}
}
})
)
*/
}
named!( pub fn search_results<'a>(input: &'a [u8]) -> IResult<&'a [u8], Vec<usize>> {
pub search_results<Vec<usize>>, alt((
|input: &'a [u8]| -> IResult<&'a [u8], Vec<usize>> {
let (input, _) = tag("* SEARCH ")(input)?;
let (input, list) = separated_nonempty_list(
tag(b" "),
map_res(is_not(" \r\n"), |s: &[u8]| {
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
}),
)(input)?;
let (input, _) = tag("\r\n")(input)?;
Ok((input, list))
},
|input: &'a [u8]| -> IResult<&'a [u8], Vec<usize>> {
let (input, _) = tag("* SEARCH\r\n")(input)?;
Ok((input, vec![]))
},
))(input)
/*
alt_complete!(do_parse!( tag!("* SEARCH ") alt_complete!(do_parse!( tag!("* SEARCH ")
>> list: separated_nonempty_list_complete!(tag!(b" "), map_res!(is_not!(" \r\n"), |s: &[u8]| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) })) >> list: separated_nonempty_list_complete!(tag!(b" "), map_res!(is_not!(" \r\n"), |s: &[u8]| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) }))
>> tag!("\r\n") >> tag!("\r\n")
>> ({ list })) | >> ({ list })) |
do_parse!(tag!("* SEARCH\r\n") >> ({ Vec::new() }))) do_parse!(tag!("* SEARCH\r\n") >> ({ Vec::new() })))
); */
}
named!( pub fn search_results_raw<'a>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8]> {
pub search_results_raw<&[u8]>, alt((
alt_complete!(do_parse!( tag!("* SEARCH ") |input: &'a [u8]| -> IResult<&'a [u8], &'a [u8]> {
>> list: take_until!("\r\n") let (input, _) = tag("* SEARCH ")(input)?;
>> tag!("\r\n") let (input, list) = take_until("\r\n")(input)?;
>> ({ list })) | let (input, _) = tag("\r\n")(input)?;
do_parse!(tag!("* SEARCH\r\n") >> ({ &b""[0..] }))) Ok((input, list))
); },
|input: &'a [u8]| -> IResult<&'a [u8], &'a [u8]> {
let (input, _) = tag("* SEARCH\r\n")(input)?;
Ok((input, &b""[0..]))
},
))(input)
/*
pub search_results_raw<&[u8]>,
alt_complete!(do_parse!( tag!("* SEARCH ")
>> list: take_until!("\r\n")
>> tag!("\r\n")
>> ({ list })) |
do_parse!(tag!("* SEARCH\r\n") >> ({ &b""[0..] })))
);
*/
}
#[test] #[test]
fn test_imap_search() { fn test_imap_search() {
assert_eq!(search_results(b"* SEARCH\r\n").to_full_result(), Ok(vec![])); assert_eq!(search_results(b"* SEARCH\r\n").map(|(_, v)| v), Ok(vec![]));
assert_eq!( assert_eq!(
search_results(b"* SEARCH 1\r\n").to_full_result(), search_results(b"* SEARCH 1\r\n").map(|(_, v)| v),
Ok(vec![1]) Ok(vec![1])
); );
assert_eq!( assert_eq!(
search_results(b"* SEARCH 1 2 3 4\r\n").to_full_result(), search_results(b"* SEARCH 1 2 3 4\r\n").map(|(_, v)| v),
Ok(vec![1, 2, 3, 4]) Ok(vec![1, 2, 3, 4])
); );
assert_eq!( assert_eq!(
search_results_raw(b"* SEARCH 1 2 3 4\r\n").to_full_result(), search_results_raw(b"* SEARCH 1 2 3 4\r\n").map(|(_, v)| v),
Ok(&b"1 2 3 4"[..]) Ok(&b"1 2 3 4"[..])
); );
} }
@ -851,7 +1046,7 @@ pub fn select_response(input: &str) -> Result<SelectResponse> {
} else if l.starts_with("* ") && l.ends_with(" RECENT") { } else if l.starts_with("* ") && l.ends_with(" RECENT") {
ret.recent = usize::from_str(&l["* ".len()..l.len() - " RECENT".len()])?; ret.recent = usize::from_str(&l["* ".len()..l.len() - " RECENT".len()])?;
} else if l.starts_with("* FLAGS (") { } else if l.starts_with("* FLAGS (") {
ret.flags = flags(&l["* FLAGS (".len()..l.len() - ")".len()]).to_full_result()?; ret.flags = flags(&l["* FLAGS (".len()..l.len() - ")".len()]).map(|(_, v)| v)?;
} else if l.starts_with("* OK [UNSEEN ") { } else if l.starts_with("* OK [UNSEEN ") {
ret.unseen = usize::from_str(&l["* OK [UNSEEN ".len()..l.find(']').unwrap()])?; ret.unseen = usize::from_str(&l["* OK [UNSEEN ".len()..l.find(']').unwrap()])?;
} else if l.starts_with("* OK [UIDVALIDITY ") { } else if l.starts_with("* OK [UIDVALIDITY ") {
@ -862,7 +1057,7 @@ pub fn select_response(input: &str) -> Result<SelectResponse> {
} else if l.starts_with("* OK [PERMANENTFLAGS (") { } else if l.starts_with("* OK [PERMANENTFLAGS (") {
ret.permanentflags = ret.permanentflags =
flags(&l["* OK [PERMANENTFLAGS (".len()..l.find(')').unwrap()]) flags(&l["* OK [PERMANENTFLAGS (".len()..l.find(')').unwrap()])
.to_full_result()?; .map(|(_, v)| v)?;
ret.can_create_flags = l.contains("\\*"); ret.can_create_flags = l.contains("\\*");
} else if l.contains("OK [READ-WRITE]") { } else if l.contains("OK [READ-WRITE]") {
ret.read_only = false; ret.read_only = false;
@ -919,15 +1114,16 @@ pub fn flags(input: &str) -> IResult<&str, (Flag, Vec<String>)> {
input = &input[match_end..]; input = &input[match_end..];
input = input.trim_start(); input = input.trim_start();
} }
IResult::Done(input, (ret, keywords)) Ok((input, (ret, keywords)))
} }
pub fn byte_flags(input: &[u8]) -> IResult<&[u8], (Flag, Vec<String>)> { pub fn byte_flags(input: &[u8]) -> IResult<&[u8], (Flag, Vec<String>)> {
let i = unsafe { std::str::from_utf8_unchecked(input) }; let i = unsafe { std::str::from_utf8_unchecked(input) };
match flags(i) { match flags(i) {
IResult::Done(rest, ret) => IResult::Done(rest.as_bytes(), ret), Ok((rest, ret)) => Ok((rest.as_bytes(), ret)),
IResult::Error(e) => IResult::Error(e), Err(nom::Err::Error((_, err))) => Err(nom::Err::Error((input, err))),
IResult::Incomplete(e) => IResult::Incomplete(e), Err(nom::Err::Failure((_, err))) => Err(nom::Err::Error((input, err))),
Err(nom::Err::Incomplete(_)) => Err(nom::Err::Error((input, nom::error::ErrorKind::Tag))),
} }
} }
@ -957,8 +1153,71 @@ pub fn byte_flags(input: &[u8]) -> IResult<&[u8], (Flag, Vec<String>)> {
* "<B27397-0100000@cac.washington.edu>") * "<B27397-0100000@cac.washington.edu>")
*/ */
named!( pub fn envelope(input: &[u8]) -> IResult<&[u8], Envelope> {
pub envelope<Envelope>, let (input, _) = tag("(")(input)?;
let (input, _) = opt(is_a("\r\n\t "))(input)?;
let (input, date) = quoted_or_nil(input)?;
let (input, _) = opt(is_a("\r\n\t "))(input)?;
let (input, subject) = quoted_or_nil(input)?;
let (input, _) = opt(is_a("\r\n\t "))(input)?;
let (input, from) = envelope_addresses(input)?;
let (input, _) = opt(is_a("\r\n\t "))(input)?;
let (input, _sender) = envelope_addresses(input)?;
let (input, _) = opt(is_a("\r\n\t "))(input)?;
let (input, _reply_to) = envelope_addresses(input)?;
let (input, _) = opt(is_a("\r\n\t "))(input)?;
let (input, to) = envelope_addresses(input)?;
let (input, _) = opt(is_a("\r\n\t "))(input)?;
let (input, cc) = envelope_addresses(input)?;
let (input, _) = opt(is_a("\r\n\t "))(input)?;
let (input, bcc) = envelope_addresses(input)?;
let (input, _) = opt(is_a("\r\n\t "))(input)?;
let (input, in_reply_to) = quoted_or_nil(input)?;
let (input, _) = opt(is_a("\r\n\t "))(input)?;
let (input, message_id) = quoted_or_nil(input)?;
let (input, _) = opt(is_a("\r\n\t "))(input)?;
let (input, _) = tag(")")(input)?;
Ok((
input,
({
let mut env = Envelope::new(0);
if let Some(date) = date {
env.set_date(&date);
if let Ok(d) = crate::email::parser::generic::date(env.date_as_str().as_bytes()) {
env.set_datetime(d);
}
}
if let Some(subject) = subject {
env.set_subject(subject.to_vec());
}
if let Some(from) = from {
env.set_from(from);
}
if let Some(to) = to {
env.set_to(to);
}
if let Some(cc) = cc {
env.set_cc(cc);
}
if let Some(bcc) = bcc {
env.set_bcc(bcc);
}
if let Some(in_reply_to) = in_reply_to {
env.set_in_reply_to(&in_reply_to);
env.push_references(&in_reply_to);
}
if let Some(message_id) = message_id {
env.set_message_id(&message_id);
}
env
}),
))
/*
do_parse!( do_parse!(
tag!("(") tag!("(")
>> opt!(is_a!("\r\n\t ")) >> opt!(is_a!("\r\n\t "))
@ -987,7 +1246,7 @@ named!(
let mut env = Envelope::new(0); let mut env = Envelope::new(0);
if let Some(date) = date { if let Some(date) = date {
env.set_date(&date); env.set_date(&date);
if let Ok(d) = crate::email::parser::date(env.date_as_str().as_bytes()) { if let Ok(d) = crate::email::parser::generic::date(env.date_as_str().as_bytes()) {
env.set_datetime(d); env.set_datetime(d);
} }
} }
@ -1020,7 +1279,8 @@ named!(
} }
env env
}) })
)); */
}
/* Helper to build StrBuilder for Address structs */ /* Helper to build StrBuilder for Address structs */
macro_rules! str_builder { macro_rules! str_builder {
@ -1033,94 +1293,179 @@ macro_rules! str_builder {
} }
// Parse a list of addresses in the format of the ENVELOPE structure // Parse a list of addresses in the format of the ENVELOPE structure
named!(pub envelope_addresses<Option<Vec<Address>>>, pub fn envelope_addresses<'a>(input: &'a [u8]) -> IResult<&'a [u8], Option<Vec<Address>>> {
alt_complete!(map!(tag!("NIL"), |_| None) | alt((
do_parse!( map(tag("NIL"), |_| None),
tag!("(") |input: &'a [u8]| -> IResult<&'a [u8], Option<Vec<Address>>> {
>> envelopes: many1!(delimited!(ws!(tag!("(")), envelope_address, tag!(")"))) let (input, _) = tag("(")(input)?;
>> tag!(")") let (input, envelopes) =
>> ({ many1(delimited(tag("("), envelope_address, tag(")")))(input.ltrim())?;
Some(envelopes) let (input, _) = tag(")")(input)?;
}) Ok((input, Some(envelopes)))
) },
)); ))(input)
/*
alt_complete!(map!(tag!("NIL"), |_| None) |
do_parse!(
tag!("(")
>> envelopes: many1!(delimited!(ws!(tag!("(")), envelope_address, tag!(")")))
>> tag!(")")
>> ({
Some(envelopes)
})
)
));
*/
}
// Parse an address in the format of the ENVELOPE structure eg // Parse an address in the format of the ENVELOPE structure eg
// ("Terry Gray" NIL "gray" "cac.washington.edu") // ("Terry Gray" NIL "gray" "cac.washington.edu")
named!( pub fn envelope_address(input: &[u8]) -> IResult<&[u8], Address> {
pub envelope_address<Address>, let (input, name) = alt((quoted, map(tag("NIL"), |_| Vec::new())))(input)?;
do_parse!( let (input, _) = is_a("\r\n\t ")(input)?;
name: alt_complete!(quoted | map!(tag!("NIL"), |_| Vec::new())) let (input, _) = alt((quoted, map(tag("NIL"), |_| Vec::new())))(input)?;
>> is_a!("\r\n\t ") let (input, _) = is_a("\r\n\t ")(input)?;
>> alt_complete!(quoted| map!(tag!("NIL"), |_| Vec::new())) let (input, mailbox_name) = alt((quoted, map(tag("NIL"), |_| Vec::new())))(input)?;
>> is_a!("\r\n\t ") let (input, _) = is_a("\r\n\t ")(input)?;
>> mailbox_name: dbg_dmp!(alt_complete!(quoted | map!(tag!("NIL"), |_| Vec::new()))) let (input, host_name) = alt((quoted, map(tag("NIL"), |_| Vec::new())))(input)?;
>> is_a!("\r\n\t ") Ok((
>> host_name: alt_complete!(quoted | map!(tag!("NIL"), |_| Vec::new())) input,
>> ({
Address::Mailbox(MailboxAddress { Address::Mailbox(MailboxAddress {
raw: format!("{}{}<{}@{}>", to_str!(&name), if name.is_empty() { "" } else { " " }, to_str!(&mailbox_name), to_str!(&host_name)).into_bytes(), raw: format!(
"{}{}<{}@{}>",
to_str!(&name),
if name.is_empty() { "" } else { " " },
to_str!(&mailbox_name),
to_str!(&host_name)
)
.into_bytes(),
display_name: str_builder!(0, name.len()), display_name: str_builder!(0, name.len()),
address_spec: str_builder!(if name.is_empty() { 1 } else { name.len() + 2 }, mailbox_name.len() + host_name.len() + 1), address_spec: str_builder!(
if name.is_empty() { 1 } else { name.len() + 2 },
mailbox_name.len() + host_name.len() + 1
),
}),
))
/*
do_parse!(
name: alt_complete!(quoted | map!(tag!("NIL"), |_| Vec::new()))
>> is_a!("\r\n\t ")
>> alt_complete!(quoted| map!(tag!("NIL"), |_| Vec::new()))
>> is_a!("\r\n\t ")
>> mailbox_name: dbg_dmp!(alt_complete!(quoted | map!(tag!("NIL"), |_| Vec::new())))
>> is_a!("\r\n\t ")
>> host_name: alt_complete!(quoted | map!(tag!("NIL"), |_| Vec::new()))
>> ({
Address::Mailbox(MailboxAddress {
raw: format!("{}{}<{}@{}>", to_str!(&name), if name.is_empty() { "" } else { " " }, to_str!(&mailbox_name), to_str!(&host_name)).into_bytes(),
display_name: str_builder!(0, name.len()),
address_spec: str_builder!(if name.is_empty() { 1 } else { name.len() + 2 }, mailbox_name.len() + host_name.len() + 1),
})
}) })
}) ));
)); */
}
// Read a literal ie a byte sequence prefixed with a tag containing its length delimited in {}s // Read a literal ie a byte sequence prefixed with a tag containing its length delimited in {}s
named!(pub literal<&[u8]>,length_bytes!(delimited!(tag!("{"), map_res!(digit, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) }), tag!("}\r\n")))); pub fn literal(input: &[u8]) -> IResult<&[u8], &[u8]> {
length_data(delimited(
tag("{"),
map_res(digit1, |s| {
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
}),
tag("}\r\n"),
))(input)
}
// Return a byte sequence surrounded by "s and decoded if necessary // Return a byte sequence surrounded by "s and decoded if necessary
pub fn quoted(input: &[u8]) -> IResult<&[u8], Vec<u8>> { pub fn quoted(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
if let IResult::Done(r, o) = literal(input) { if let Ok((r, o)) = literal(input) {
return match crate::email::parser::phrase(o, false) { return match crate::email::parser::encodings::phrase(o, false) {
IResult::Done(_, out) => IResult::Done(r, out), Ok((_, out)) => Ok((r, out)),
e => e, e => e,
}; };
} }
if input.is_empty() || input[0] != b'"' { if input.is_empty() || input[0] != b'"' {
return IResult::Error(nom::ErrorKind::Custom(0)); return Err(nom::Err::Error((input, nom::error::ErrorKind::Tag)));
} }
let mut i = 1; let mut i = 1;
while i < input.len() { while i < input.len() {
if input[i] == b'\"' && input[i - 1] != b'\\' { if input[i] == b'\"' && input[i - 1] != b'\\' {
return match crate::email::parser::phrase(&input[1..i], false) { return match crate::email::parser::encodings::phrase(&input[1..i], false) {
IResult::Done(_, out) => IResult::Done(&input[i + 1..], out), Ok((_, out)) => Ok((&input[i + 1..], out)),
e => e, e => e,
}; };
} }
i += 1; i += 1;
} }
return IResult::Error(nom::ErrorKind::Custom(0)); return Err(nom::Err::Error((input, nom::error::ErrorKind::Tag)));
} }
named!( pub fn quoted_or_nil(input: &[u8]) -> IResult<&[u8], Option<Vec<u8>>> {
pub quoted_or_nil<Option<Vec<u8>>>, alt((map(tag("NIL"), |_| None), map(quoted, |v| Some(v))))(input.ltrim())
/*
alt_complete!(map!(ws!(tag!("NIL")), |_| None) | map!(quoted, |v| Some(v)))); alt_complete!(map!(ws!(tag!("NIL")), |_| None) | map!(quoted, |v| Some(v))));
*/
}
named!( pub fn uid_fetch_envelopes_response(
pub uid_fetch_envelopes_response<Vec<(usize, Option<(Flag, Vec<String>)>, Envelope)>>, input: &[u8],
many0!( ) -> IResult<&[u8], Vec<(usize, Option<(Flag, Vec<String>)>, Envelope)>> {
do_parse!( many0(
tag!("* ") |input: &[u8]| -> IResult<&[u8], (usize, Option<(Flag, Vec<String>)>, Envelope)> {
>> take_while!(call!(is_digit)) let (input, _) = tag("* ")(input)?;
>> tag!(" FETCH (") let (input, _) = take_while(is_digit)(input)?;
>> uid_flags: permutation!(preceded!(ws!(tag!("UID ")), map_res!(digit, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) })), opt!(preceded!(ws!(tag!("FLAGS ")), delimited!(tag!("("), byte_flags, tag!(")"))))) let (input, _) = tag(" FETCH (")(input)?;
>> tag!(" ENVELOPE ") let (input, uid_flags) = permutation((
>> env: ws!(envelope) preceded(
>> tag!("BODYSTRUCTURE ") tag("UID "),
>> bodystructure: take_until!(")\r\n") map_res(digit1, |s| {
>> tag!(")\r\n") usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
>> ({ }),
),
opt(preceded(
tag("FLAGS "),
delimited(tag("("), byte_flags, tag(")")),
)),
))(input.ltrim())?;
let (input, _) = tag(" ENVELOPE ")(input)?;
let (input, env) = envelope(input.ltrim())?;
let (input, _) = tag("BODYSTRUCTURE ")(input)?;
let (input, bodystructure) = take_until(")\r\n")(input)?;
let (input, _) = tag(")\r\n")(input)?;
Ok((input, {
let mut env = env; let mut env = env;
let has_attachments = bodystructure_has_attachments(bodystructure); let has_attachments = bodystructure_has_attachments(bodystructure);
env.set_has_attachments(has_attachments); env.set_has_attachments(has_attachments);
(uid_flags.0, uid_flags.1, env) (uid_flags.0, uid_flags.1, env)
}) }))
},
)(input)
/*
many0!(
do_parse!(
tag!("* ")
>> take_while!(call!(is_digit))
>> tag!(" FETCH (")
>> uid_flags: permutation!(preceded!(ws!(tag!("UID ")), map_res!(digit1, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) })), opt!(preceded!(ws!(tag!("FLAGS ")), delimited!(tag!("("), byte_flags, tag!(")")))))
>> tag!(" ENVELOPE ")
>> env: ws!(envelope)
>> tag!("BODYSTRUCTURE ")
>> bodystructure: take_until!(")\r\n")
>> tag!(")\r\n")
>> ({
let mut env = env;
let has_attachments = bodystructure_has_attachments(bodystructure);
env.set_has_attachments(has_attachments);
(uid_flags.0, uid_flags.1, env)
})
)
) )
) );
); */
}
pub fn bodystructure_has_attachments(input: &[u8]) -> bool { pub fn bodystructure_has_attachments(input: &[u8]) -> bool {
input.rfind(b" \"mixed\" ").is_some() || input.rfind(b" \"MIXED\" ").is_some() input.rfind(b" \"mixed\" ").is_some() || input.rfind(b" \"MIXED\" ").is_some()

View File

@ -62,7 +62,7 @@ impl ImapConnection {
let mut response = String::with_capacity(8 * 1024); let mut response = String::with_capacity(8 * 1024);
let untagged_response = let untagged_response =
match super::protocol_parser::untagged_responses(line.as_bytes()).to_full_result() { match super::protocol_parser::untagged_responses(line.as_bytes()).map(|(_, v)| v) {
Ok(None) | Err(_) => { Ok(None) | Err(_) => {
return Ok(false); return Ok(false);
} }
@ -162,7 +162,7 @@ impl ImapConnection {
self.read_response(&mut response, RequiredResponses::SEARCH) self.read_response(&mut response, RequiredResponses::SEARCH)
); );
match super::protocol_parser::search_results_raw(response.as_bytes()) match super::protocol_parser::search_results_raw(response.as_bytes())
.to_full_result() .map(|(_, v)| v)
.map_err(MeliError::from) .map_err(MeliError::from)
{ {
Ok(&[]) => { Ok(&[]) => {
@ -267,7 +267,7 @@ impl ImapConnection {
match super::protocol_parser::search_results( match super::protocol_parser::search_results(
response.split_rn().next().unwrap_or("").as_bytes(), response.split_rn().next().unwrap_or("").as_bytes(),
) )
.to_full_result() .map(|(_, v)| v)
{ {
Ok(mut v) => { Ok(mut v) => {
if let Some(uid) = v.pop() { if let Some(uid) = v.pop() {

View File

@ -271,7 +271,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
} }
*uid_store.is_online.lock().unwrap() = (Instant::now(), Ok(())); *uid_store.is_online.lock().unwrap() = (Instant::now(), Ok(()));
match protocol_parser::untagged_responses(line.as_slice()) match protocol_parser::untagged_responses(line.as_slice())
.to_full_result() .map(|(_, v)| v)
.map_err(MeliError::from) .map_err(MeliError::from)
{ {
Ok(Some(Recent(r))) => { Ok(Some(Recent(r))) => {
@ -292,7 +292,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
conn.read_response(&mut response, RequiredResponses::SEARCH) conn.read_response(&mut response, RequiredResponses::SEARCH)
); );
match protocol_parser::search_results_raw(response.as_bytes()) match protocol_parser::search_results_raw(response.as_bytes())
.to_full_result() .map(|(_, v)| v)
.map_err(MeliError::from) .map_err(MeliError::from)
{ {
Ok(&[]) => { Ok(&[]) => {
@ -529,7 +529,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
conn.read_response(&mut response, RequiredResponses::SEARCH) conn.read_response(&mut response, RequiredResponses::SEARCH)
); );
match search_results(response.split_rn().next().unwrap_or("").as_bytes()) match search_results(response.split_rn().next().unwrap_or("").as_bytes())
.to_full_result() .map(|(_, v)| v)
{ {
Ok(mut v) => { Ok(mut v) => {
if let Some(uid) = v.pop() { if let Some(uid) = v.pop() {
@ -646,7 +646,7 @@ pub fn examine_updates(
conn.read_response(&mut response, RequiredResponses::SEARCH) conn.read_response(&mut response, RequiredResponses::SEARCH)
); );
match protocol_parser::search_results_raw(response.as_bytes()) match protocol_parser::search_results_raw(response.as_bytes())
.to_full_result() .map(|(_, v)| v)
.map_err(MeliError::from) .map_err(MeliError::from)
{ {
Ok(&[]) => { Ok(&[]) => {

View File

@ -231,7 +231,7 @@ impl std::fmt::Display for EmailAddress {
impl std::convert::From<EmailObject> for crate::Envelope { impl std::convert::From<EmailObject> for crate::Envelope {
fn from(mut t: EmailObject) -> crate::Envelope { fn from(mut t: EmailObject) -> crate::Envelope {
let mut env = crate::Envelope::new(0); let mut env = crate::Envelope::new(0);
if let Ok(d) = crate::email::parser::date(env.date_as_str().as_bytes()) { if let Ok(d) = crate::email::parser::generic::date(env.date_as_str().as_bytes()) {
env.set_datetime(d); env.set_datetime(d);
} }
if let Some(ref mut sent_at) = t.sent_at { if let Some(ref mut sent_at) = t.sent_at {
@ -249,9 +249,9 @@ impl std::convert::From<EmailObject> for crate::Envelope {
env.push_references(in_reply_to[0].as_bytes()); env.push_references(in_reply_to[0].as_bytes());
} }
if let Some(v) = t.headers.get("References") { if let Some(v) = t.headers.get("References") {
let parse_result = crate::email::parser::references(v.as_bytes()); let parse_result = crate::email::parser::address::references(v.as_bytes());
if parse_result.is_done() { if let Ok((_, v)) = parse_result {
for v in parse_result.to_full_result().unwrap() { for v in v {
env.push_references(v); env.push_references(v);
} }
} }
@ -259,7 +259,7 @@ impl std::convert::From<EmailObject> for crate::Envelope {
} }
if let Some(v) = t.headers.get("Date") { if let Some(v) = t.headers.get("Date") {
env.set_date(v.as_bytes()); env.set_date(v.as_bytes());
if let Ok(d) = crate::email::parser::date(v.as_bytes()) { if let Ok(d) = crate::email::parser::generic::date(v.as_bytes()) {
env.set_datetime(d); env.set_datetime(d);
} }
} }

View File

@ -38,7 +38,7 @@ use crate::get_path_hash;
use crate::shellexpand::ShellExpandTrait; use crate::shellexpand::ShellExpandTrait;
use libc; use libc;
use memmap::{Mmap, Protection}; use memmap::{Mmap, Protection};
use nom::{IResult, Needed}; use nom::{self, error::ErrorKind, IResult};
extern crate notify; extern crate notify;
use self::notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; use self::notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
use std::collections::hash_map::{DefaultHasher, HashMap}; use std::collections::hash_map::{DefaultHasher, HashMap};
@ -213,7 +213,7 @@ impl BackendOp for MboxOp {
return flags; return flags;
}; };
if let Ok(headers) = parser::headers_raw(contents.as_slice()).to_full_result() { if let Ok((_, headers)) = parser::headers::headers_raw(contents.as_slice()) {
if let Some(start) = headers.find(b"Status:") { if let Some(start) = headers.find(b"Status:") {
if let Some(end) = headers[start..].find(b"\n") { if let Some(end) = headers[start..].find(b"\n") {
let start = start + b"Status:".len(); let start = start + b"Status:".len();
@ -275,7 +275,7 @@ pub fn mbox_parse(
file_offset: usize, file_offset: usize,
) -> IResult<&[u8], Vec<Envelope>> { ) -> IResult<&[u8], Vec<Envelope>> {
if input.is_empty() { if input.is_empty() {
return IResult::Incomplete(Needed::Unknown); return Err(nom::Err::Error((input, ErrorKind::Tag)));
} }
let mut input = input; let mut input = input;
let mut offset = 0; let mut offset = 0;
@ -381,7 +381,7 @@ pub fn mbox_parse(
break; break;
} }
} }
return IResult::Done(&[], envelopes); return Ok((&[], envelopes));
} }
/// Mbox backend /// Mbox backend
@ -430,8 +430,8 @@ impl MailBackend for MboxType {
}; };
let payload = mbox_parse(index, contents.as_slice(), 0) let payload = mbox_parse(index, contents.as_slice(), 0)
.to_full_result() .map_err(|e| MeliError::from(e))
.map_err(|e| MeliError::from(e)); .map(|(_, v)| v);
{ {
let mut mailbox_lock = mailboxes.lock().unwrap(); let mut mailbox_lock = mailboxes.lock().unwrap();
mailbox_lock mailbox_lock
@ -516,13 +516,11 @@ impl MailBackend for MboxType {
if contents if contents
.starts_with(mailbox_lock[&mailbox_hash].content.as_slice()) .starts_with(mailbox_lock[&mailbox_hash].content.as_slice())
{ {
if let Ok(envelopes) = mbox_parse( if let Ok((_, envelopes)) = mbox_parse(
index.clone(), index.clone(),
&contents[mailbox_lock[&mailbox_hash].content.len()..], &contents[mailbox_lock[&mailbox_hash].content.len()..],
mailbox_lock[&mailbox_hash].content.len(), mailbox_lock[&mailbox_hash].content.len(),
) ) {
.to_full_result()
{
for env in envelopes { for env in envelopes {
sender.send(RefreshEvent { sender.send(RefreshEvent {
account_hash, account_hash,
@ -618,7 +616,7 @@ impl MailBackend for MboxType {
Err(MeliError::new("Unimplemented.")) Err(MeliError::new("Unimplemented."))
} }
fn as_any(&self) -> &dyn ::std::any::Any { fn as_any(&self) -> &dyn::std::any::Any {
self self
} }
} }

View File

@ -33,6 +33,7 @@ mod attachment_types;
pub mod attachments; pub mod attachments;
pub use crate::attachments::*; pub use crate::attachments::*;
mod address; mod address;
//pub mod parser;
pub mod parser; pub mod parser;
use crate::parser::BytesExt; use crate::parser::BytesExt;
pub use address::*; pub use address::*;
@ -243,7 +244,7 @@ impl Envelope {
bytes = &bytes[offset + 1..]; bytes = &bytes[offset + 1..];
} }
} }
let (headers, body) = match parser::mail(bytes).to_full_result() { let (headers, body) = match parser::mail(bytes) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
debug!("error in parsing mail\n{:?}\n", e); debug!("error in parsing mail\n{:?}\n", e);
@ -257,51 +258,50 @@ impl Envelope {
self.other_headers.insert( self.other_headers.insert(
String::from_utf8(name.to_vec()) String::from_utf8(name.to_vec())
.unwrap_or_else(|err| String::from_utf8_lossy(&err.into_bytes()).into()), .unwrap_or_else(|err| String::from_utf8_lossy(&err.into_bytes()).into()),
parser::phrase(value, false) parser::encodings::phrase(value, false)
.to_full_result() .map(|(_, value)| {
.map(|value| {
String::from_utf8(value) String::from_utf8(value)
.unwrap_or_else(|err| String::from_utf8_lossy(&err.into_bytes()).into()) .unwrap_or_else(|err| String::from_utf8_lossy(&err.into_bytes()).into())
}) })
.unwrap_or_else(|_| String::from_utf8_lossy(value).into()), .unwrap_or_else(|_| String::from_utf8_lossy(value).into()),
); );
if name.eq_ignore_ascii_case(b"to") { if name.eq_ignore_ascii_case(b"to") {
let parse_result = parser::rfc2822address_list(value); let parse_result = parser::address::rfc2822address_list(value);
if parse_result.is_done() { if parse_result.is_ok() {
let value = parse_result.to_full_result().unwrap(); let value = parse_result.unwrap().1;
self.set_to(value); self.set_to(value);
}; };
} else if name.eq_ignore_ascii_case(b"cc") { } else if name.eq_ignore_ascii_case(b"cc") {
let parse_result = parser::rfc2822address_list(value); let parse_result = parser::address::rfc2822address_list(value);
if parse_result.is_done() { if parse_result.is_ok() {
let value = parse_result.to_full_result().unwrap(); let value = parse_result.unwrap().1;
self.set_cc(value); self.set_cc(value);
}; };
} else if name.eq_ignore_ascii_case(b"bcc") { } else if name.eq_ignore_ascii_case(b"bcc") {
let parse_result = parser::rfc2822address_list(value); let parse_result = parser::address::rfc2822address_list(value);
if parse_result.is_done() { if parse_result.is_ok() {
let value = parse_result.to_full_result().unwrap(); let value = parse_result.unwrap().1;
self.set_bcc(value); self.set_bcc(value);
}; };
} else if name.eq_ignore_ascii_case(b"from") { } else if name.eq_ignore_ascii_case(b"from") {
let parse_result = parser::rfc2822address_list(value); let parse_result = parser::address::rfc2822address_list(value);
if parse_result.is_done() { if parse_result.is_ok() {
let value = parse_result.to_full_result().unwrap(); let value = parse_result.unwrap().1;
self.set_from(value); self.set_from(value);
} }
} else if name.eq_ignore_ascii_case(b"subject") { } else if name.eq_ignore_ascii_case(b"subject") {
let parse_result = parser::phrase(value.trim(), false); let parse_result = parser::encodings::phrase(value.trim(), false);
if parse_result.is_done() { if parse_result.is_ok() {
let value = parse_result.to_full_result().unwrap(); let value = parse_result.unwrap().1;
self.set_subject(value); self.set_subject(value);
}; };
} else if name.eq_ignore_ascii_case(b"message-id") { } else if name.eq_ignore_ascii_case(b"message-id") {
self.set_message_id(value); self.set_message_id(value);
} else if name.eq_ignore_ascii_case(b"references") { } else if name.eq_ignore_ascii_case(b"references") {
{ {
let parse_result = parser::references(value); let parse_result = parser::address::references(value);
if parse_result.is_done() { if parse_result.is_ok() {
for v in parse_result.to_full_result().unwrap() { for v in parse_result.unwrap().1 {
self.push_references(v); self.push_references(v);
} }
} }
@ -311,16 +311,16 @@ impl Envelope {
self.set_in_reply_to(value); self.set_in_reply_to(value);
in_reply_to = Some(value); in_reply_to = Some(value);
} else if name.eq_ignore_ascii_case(b"date") { } else if name.eq_ignore_ascii_case(b"date") {
let parse_result = parser::phrase(value, false); let parse_result = parser::encodings::phrase(value, false);
if parse_result.is_done() { if parse_result.is_ok() {
let value = parse_result.to_full_result().unwrap(); let value = parse_result.unwrap().1;
self.set_date(value.as_slice()); self.set_date(value.as_slice());
} else { } else {
self.set_date(value); self.set_date(value);
} }
} else if name.eq_ignore_ascii_case(b"content-type") { } else if name.eq_ignore_ascii_case(b"content-type") {
match parser::content_type(value).to_full_result() { match parser::attachments::content_type(value) {
Ok((ct, cst, ref params)) Ok((_, (ct, cst, ref params)))
if ct.eq_ignore_ascii_case(b"multipart") if ct.eq_ignore_ascii_case(b"multipart")
&& cst.eq_ignore_ascii_case(b"mixed") => && cst.eq_ignore_ascii_case(b"mixed") =>
{ {
@ -352,7 +352,7 @@ impl Envelope {
if let Some(ref mut x) = in_reply_to { if let Some(ref mut x) = in_reply_to {
self.push_references(x); self.push_references(x);
} }
if let Ok(d) = parser::date(&self.date.as_bytes()) { if let Ok(d) = parser::generic::date(&self.date.as_bytes()) {
self.set_datetime(d); self.set_datetime(d);
} }
if self.message_id.raw().is_empty() { if self.message_id.raw().is_empty() {
@ -425,7 +425,7 @@ impl Envelope {
/// Requests bytes from backend and thus can fail /// Requests bytes from backend and thus can fail
pub fn headers<'a>(&self, bytes: &'a [u8]) -> Result<Vec<(&'a str, &'a str)>> { pub fn headers<'a>(&self, bytes: &'a [u8]) -> Result<Vec<(&'a str, &'a str)>> {
let ret = parser::headers(bytes).to_full_result()?; let ret = parser::headers::headers(bytes)?.1;
let len = ret.len(); let len = ret.len();
ret.into_iter() ret.into_iter()
.try_fold(Vec::with_capacity(len), |mut acc, (a, b)| { .try_fold(Vec::with_capacity(len), |mut acc, (a, b)| {
@ -491,8 +491,8 @@ impl Envelope {
self.to = new_val; self.to = new_val;
} }
pub fn set_in_reply_to(&mut self, new_val: &[u8]) { pub fn set_in_reply_to(&mut self, new_val: &[u8]) {
let slice = match parser::message_id(new_val).to_full_result() { let slice = match parser::address::message_id(new_val) {
Ok(v) => v, Ok(v) => v.1,
Err(_) => { Err(_) => {
self.in_reply_to = None; self.in_reply_to = None;
return; return;
@ -515,8 +515,8 @@ impl Envelope {
self.subject = Some(new_val); self.subject = Some(new_val);
} }
pub fn set_message_id(&mut self, new_val: &[u8]) { pub fn set_message_id(&mut self, new_val: &[u8]) {
match parser::message_id(new_val).to_full_result() { match parser::address::message_id(new_val) {
Ok(slice) => { Ok((_, slice)) => {
self.message_id = MessageID::new(new_val, slice); self.message_id = MessageID::new(new_val, slice);
} }
Err(_) => { Err(_) => {
@ -525,8 +525,8 @@ impl Envelope {
} }
} }
pub fn push_references(&mut self, new_val: &[u8]) { pub fn push_references(&mut self, new_val: &[u8]) {
let slice = match parser::message_id(new_val).to_full_result() { let slice = match parser::address::message_id(new_val) {
Ok(v) => v, Ok(v) => v.1,
Err(e) => { Err(e) => {
debug!(e); debug!(e);
return; return;

View File

@ -218,7 +218,7 @@ impl StrBuild for MessageID {
#[test] #[test]
fn test_strbuilder() { fn test_strbuilder() {
let m_id = b"<20170825132332.6734-1@el13635@mail.ntua.gr>"; let m_id = b"<20170825132332.6734-1@el13635@mail.ntua.gr>";
let (_, slice) = parser::message_id(m_id).unwrap(); let (_, slice) = parser::address::message_id(m_id).unwrap();
assert_eq!( assert_eq!(
MessageID::new(m_id, slice), MessageID::new(m_id, slice),
MessageID( MessageID(

View File

@ -39,8 +39,8 @@ pub struct AttachmentBuilder {
impl AttachmentBuilder { impl AttachmentBuilder {
pub fn new(content: &[u8]) -> Self { pub fn new(content: &[u8]) -> Self {
let (headers, body) = match parser::attachment(content).to_full_result() { let (headers, body) = match parser::attachments::attachment(content) {
Ok(v) => v, Ok((_, v)) => v,
Err(_) => { Err(_) => {
debug!("error in parsing attachment"); debug!("error in parsing attachment");
debug!("\n-------------------------------"); debug!("\n-------------------------------");
@ -121,8 +121,8 @@ impl AttachmentBuilder {
} }
pub fn set_content_type_from_bytes(&mut self, value: &[u8]) -> &mut Self { pub fn set_content_type_from_bytes(&mut self, value: &[u8]) -> &mut Self {
match parser::content_type(value).to_full_result() { match parser::attachments::content_type(value) {
Ok((ct, cst, params)) => { Ok((_, (ct, cst, params))) => {
if ct.eq_ignore_ascii_case(b"multipart") { if ct.eq_ignore_ascii_case(b"multipart") {
let mut boundary = None; let mut boundary = None;
for (n, v) in params { for (n, v) in params {
@ -185,10 +185,9 @@ impl AttachmentBuilder {
let mut name: Option<String> = None; let mut name: Option<String> = None;
for (n, v) in params { for (n, v) in params {
if n.eq_ignore_ascii_case(b"name") { if n.eq_ignore_ascii_case(b"name") {
if let Ok(v) = crate::email::parser::phrase(v.trim(), false) if let Ok(v) = crate::email::parser::encodings::phrase(v.trim(), false)
.to_full_result()
.as_ref() .as_ref()
.and_then(|r| Ok(String::from_utf8_lossy(r).to_string())) .and_then(|(_, r)| Ok(String::from_utf8_lossy(r).to_string()))
{ {
name = Some(v); name = Some(v);
} else { } else {
@ -229,13 +228,13 @@ impl AttachmentBuilder {
return Vec::new(); return Vec::new();
} }
match parser::parts(raw, boundary).to_full_result() { match parser::attachments::parts(raw, boundary) {
Ok(attachments) => { Ok((_, attachments)) => {
let mut vec = Vec::with_capacity(attachments.len()); let mut vec = Vec::with_capacity(attachments.len());
for a in attachments { for a in attachments {
let mut builder = AttachmentBuilder::default(); let mut builder = AttachmentBuilder::default();
let (headers, body) = match parser::attachment(&a).to_full_result() { let (headers, body) = match parser::attachments::attachment(&a) {
Ok(v) => v, Ok((_, v)) => v,
Err(_) => { Err(_) => {
debug!("error in parsing attachment"); debug!("error in parsing attachment");
debug!("\n-------------------------------"); debug!("\n-------------------------------");
@ -420,8 +419,8 @@ impl Attachment {
match self.content_type { match self.content_type {
ContentType::Multipart { ref boundary, .. } => { ContentType::Multipart { ref boundary, .. } => {
match parser::multipart_parts(self.body(), boundary).to_full_result() { match parser::attachments::multipart_parts(self.body(), boundary) {
Ok(v) => v, Ok((_, v)) => v,
Err(e) => { Err(e) => {
debug!("error in parsing attachment"); debug!("error in parsing attachment");
debug!("\n-------------------------------"); debug!("\n-------------------------------");
@ -444,10 +443,12 @@ impl Attachment {
} }
// FIXME: check if any part is multipart/mixed as well // FIXME: check if any part is multipart/mixed as well
match parser::multipart_parts(bytes, boundary).to_full_result() { match parser::attachments::multipart_parts(bytes, boundary) {
Ok(parts) => { Ok((_, parts)) => {
for p in parts { for p in parts {
for (n, v) in crate::email::parser::HeaderIterator(p.display_bytes(bytes)) { for (n, v) in
crate::email::parser::generic::HeaderIterator(p.display_bytes(bytes))
{
if !n.eq_ignore_ascii_case(b"content-type") && !v.starts_with(b"text/") { if !n.eq_ignore_ascii_case(b"content-type") && !v.starts_with(b"text/") {
return true; return true;
} }
@ -655,14 +656,14 @@ impl Attachment {
pub fn parameters(&self) -> Vec<(&[u8], &[u8])> { pub fn parameters(&self) -> Vec<(&[u8], &[u8])> {
let mut ret = Vec::new(); let mut ret = Vec::new();
let (headers, _) = match parser::attachment(&self.raw).to_full_result() { let (headers, _) = match parser::attachments::attachment(&self.raw) {
Ok(v) => v, Ok((_, v)) => v,
Err(_) => return ret, Err(_) => return ret,
}; };
for (name, value) in headers { for (name, value) in headers {
if name.eq_ignore_ascii_case(b"content-type") { if name.eq_ignore_ascii_case(b"content-type") {
match parser::content_type(value).to_full_result() { match parser::attachments::content_type(value) {
Ok((_, _, params)) => { Ok((_, (_, _, params))) => {
ret = params; ret = params;
} }
_ => {} _ => {}
@ -751,16 +752,18 @@ fn decode_helper<'a>(a: &'a Attachment, filter: &mut Option<Filter<'a>>) -> Vec<
Ok(v) => v, Ok(v) => v,
_ => a.body().to_vec(), _ => a.body().to_vec(),
}, },
ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_bytes(a.body()) ContentTransferEncoding::QuotedPrintable => {
.to_full_result() parser::encodings::quoted_printable_bytes(a.body())
.unwrap(), .unwrap()
.1
}
ContentTransferEncoding::_7Bit ContentTransferEncoding::_7Bit
| ContentTransferEncoding::_8Bit | ContentTransferEncoding::_8Bit
| ContentTransferEncoding::Other { .. } => a.body().to_vec(), | ContentTransferEncoding::Other { .. } => a.body().to_vec(),
}; };
let mut ret = if a.content_type.is_text() { let mut ret = if a.content_type.is_text() {
if let Ok(v) = parser::decode_charset(&bytes, charset) { if let Ok(v) = parser::encodings::decode_charset(&bytes, charset) {
v.into_bytes() v.into_bytes()
} else { } else {
a.body().to_vec() a.body().to_vec()

View File

@ -88,7 +88,7 @@ impl str::FromStr for Draft {
return Err(MeliError::new("Empty input in Draft::from_str")); return Err(MeliError::new("Empty input in Draft::from_str"));
} }
let (headers, _) = parser::mail(s.as_bytes()).to_full_result()?; let (headers, _) = parser::mail(s.as_bytes())?;
let mut ret = Draft::default(); let mut ret = Draft::default();
for (k, v) in headers { for (k, v) in headers {
@ -107,9 +107,7 @@ impl str::FromStr for Draft {
} }
} }
if ret.headers.contains_key("From") && !ret.headers.contains_key("Message-ID") { if ret.headers.contains_key("From") && !ret.headers.contains_key("Message-ID") {
if let super::parser::IResult::Done(_, addr) = if let Ok((_, addr)) = super::parser::address::mailbox(ret.headers["From"].as_bytes()) {
super::parser::mailbox(ret.headers["From"].as_bytes())
{
if let Some(fqdn) = addr.get_fqdn() { if let Some(fqdn) = addr.get_fqdn() {
if ret if ret
.headers .headers
@ -256,8 +254,7 @@ impl Draft {
let mut ret = String::new(); let mut ret = String::new();
if self.headers.contains_key("From") && !self.headers.contains_key("Message-ID") { if self.headers.contains_key("From") && !self.headers.contains_key("Message-ID") {
if let super::parser::IResult::Done(_, addr) = if let Ok((_, addr)) = super::parser::address::mailbox(self.headers["From"].as_bytes())
super::parser::mailbox(self.headers["From"].as_bytes())
{ {
if let Some(fqdn) = addr.get_fqdn() { if let Some(fqdn) = addr.get_fqdn() {
if self if self

View File

@ -162,9 +162,9 @@ fn test_encode_header() {
); );
assert_eq!( assert_eq!(
&std::str::from_utf8( &std::str::from_utf8(
&crate::email::parser::phrase(encode_header(&words).as_bytes(), false) &crate::email::parser::encodings::phrase(encode_header(&words).as_bytes(), false)
.to_full_result()
.unwrap() .unwrap()
.1
) )
.unwrap(), .unwrap(),
&words, &words,
@ -175,9 +175,9 @@ fn test_encode_header() {
assert_eq!( assert_eq!(
r#"[internal] Νέος Οδηγός Συγγραφής"#, r#"[internal] Νέος Οδηγός Συγγραφής"#,
std::str::from_utf8( std::str::from_utf8(
&crate::email::parser::phrase(encode_header(&words_enc).as_bytes(), false) &crate::email::parser::encodings::phrase(encode_header(&words_enc).as_bytes(), false)
.to_full_result()
.unwrap() .unwrap()
.1
) )
.unwrap(), .unwrap(),
); );
@ -186,9 +186,9 @@ fn test_encode_header() {
assert_eq!( assert_eq!(
"[Advcomparch] Συμπεριφορά σε flush λόγω misprediction κατά την εκτέλεση store", "[Advcomparch] Συμπεριφορά σε flush λόγω misprediction κατά την εκτέλεση store",
std::str::from_utf8( std::str::from_utf8(
&crate::email::parser::phrase(encode_header(&words_enc).as_bytes(), false) &crate::email::parser::encodings::phrase(encode_header(&words_enc).as_bytes(), false)
.to_full_result()
.unwrap() .unwrap()
.1
) )
.unwrap(), .unwrap(),
); );

View File

@ -48,8 +48,8 @@ impl<'a> From<&'a [u8]> for ListAction<'a> {
impl<'a> ListAction<'a> { impl<'a> ListAction<'a> {
pub fn parse_options_list(input: &'a [u8]) -> Option<SmallVec<[ListAction<'a>; 4]>> { pub fn parse_options_list(input: &'a [u8]) -> Option<SmallVec<[ListAction<'a>; 4]>> {
parser::angle_bracket_delimeted_list(input) parser::generic::angle_bracket_delimeted_list(input)
.map(|mut vec| { .map(|(_, mut vec)| {
/* Prefer email options first, since this _is_ a mail client after all and it's /* Prefer email options first, since this _is_ a mail client after all and it's
* more automated */ * more automated */
vec.sort_unstable_by(|a, b| { vec.sort_unstable_by(|a, b| {
@ -64,7 +64,6 @@ impl<'a> ListAction<'a> {
.map(|elem| ListAction::from(elem)) .map(|elem| ListAction::from(elem))
.collect::<SmallVec<[ListAction<'a>; 4]>>() .collect::<SmallVec<[ListAction<'a>; 4]>>()
}) })
.to_full_result()
.ok() .ok()
} }
} }

View File

@ -54,7 +54,7 @@ impl TryFrom<&[u8]> for Mailto {
type Error = String; type Error = String;
fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> { fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
let parse_res = super::parser::mailto(value).to_full_result(); let parse_res = super::parser::generic::mailto(value).map(|(_, v)| v);
if parse_res.is_ok() { if parse_res.is_ok() {
Ok(parse_res.unwrap()) Ok(parse_res.unwrap())
} else { } else {
@ -74,8 +74,8 @@ mod tests {
#[test] #[test]
fn test_mailto() { fn test_mailto() {
let test_address = super::parser::address(b"info@example.com") let test_address = super::parser::address::address(b"info@example.com")
.to_full_result() .map(|(_, v)| v)
.unwrap(); .unwrap();
let mailto = Mailto::try_from(&b"mailto:info@example.com?subject=email%20subject"[0..]) let mailto = Mailto::try_from(&b"mailto:info@example.com?subject=email%20subject"[0..])
.expect("Could not parse mailto link."); .expect("Could not parse mailto link.");

File diff suppressed because it is too large Load Diff

View File

@ -32,8 +32,6 @@ use std::str;
use std::string; use std::string;
use std::sync::Arc; use std::sync::Arc;
use nom;
pub type Result<T> = result::Result<T, MeliError>; pub type Result<T> = result::Result<T, MeliError>;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -139,13 +137,6 @@ impl From<io::Error> for MeliError {
} }
} }
impl From<nom::IError> for MeliError {
#[inline]
fn from(kind: nom::IError) -> MeliError {
MeliError::new(format!("{:?}", kind))
}
}
impl<'a> From<Cow<'a, str>> for MeliError { impl<'a> From<Cow<'a, str>> for MeliError {
#[inline] #[inline]
fn from(kind: Cow<'_, str>) -> MeliError { fn from(kind: Cow<'_, str>) -> MeliError {
@ -261,3 +252,19 @@ impl From<String> for MeliError {
MeliError::new(kind) MeliError::new(kind)
} }
} }
impl From<nom::Err<(&[u8], nom::error::ErrorKind)>> for MeliError {
#[inline]
fn from(kind: nom::Err<(&[u8], nom::error::ErrorKind)>) -> MeliError {
MeliError::new("Parsing error")
.set_source(Some(Arc::new(MeliError::new(format!("{}", kind)))))
}
}
impl From<nom::Err<(&str, nom::error::ErrorKind)>> for MeliError {
#[inline]
fn from(kind: nom::Err<(&str, nom::error::ErrorKind)>) -> MeliError {
MeliError::new("Parsing error")
.set_source(Some(Arc::new(MeliError::new(format!("{}", kind)))))
}
}

View File

@ -122,10 +122,9 @@ pub mod sqlite3;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
/* parser */ /* parser */
#[macro_use]
extern crate nom;
extern crate data_encoding; extern crate data_encoding;
extern crate encoding; extern crate encoding;
pub use nom;
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;

View File

@ -197,9 +197,8 @@ impl Composer {
if let Some(actions) = list_management::ListActions::detect(&parent_message) { if let Some(actions) = list_management::ListActions::detect(&parent_message) {
if let Some(post) = actions.post { if let Some(post) = actions.post {
if let list_management::ListAction::Email(list_post_addr) = post[0] { if let list_management::ListAction::Email(list_post_addr) = post[0] {
if let Ok(list_address) = melib::email::parser::mailto(list_post_addr) if let Ok(list_address) = melib::email::parser::generic::mailto(list_post_addr)
.to_full_result() .map(|(_, m)| m.address)
.map(|m| m.address)
{ {
let list_address_string = list_address.to_string(); let list_address_string = list_address.to_string();
ret.mode = ViewMode::SelectRecipients(UIDialog::new( ret.mode = ViewMode::SelectRecipients(UIDialog::new(

View File

@ -701,17 +701,16 @@ impl Component for MailView {
let mut ret = op let mut ret = op
.as_bytes() .as_bytes()
.and_then(|b| { .and_then(|b| {
melib::email::parser::headers(b) melib::email::parser::headers::headers(b)
.to_full_result() .map(|(_, v)| v)
.map_err(|err| err.into()) .map_err(|err| err.into())
}) })
.and_then(|headers| { .and_then(|headers| {
Ok(headers Ok(headers
.into_iter() .into_iter()
.map(|(h, v)| { .map(|(h, v)| {
melib::email::parser::phrase(v, true) melib::email::parser::encodings::phrase(v, true)
.to_full_result() .map(|(_, v)| {
.map(|v| {
let mut h = h.to_vec(); let mut h = h.to_vec();
h.push(b':'); h.push(b':');
h.push(b' '); h.push(b' ');
@ -1106,8 +1105,8 @@ impl Component for MailView {
let attachment_type = u.mime_type(); let attachment_type = u.mime_type();
let binary = query_default_app(&attachment_type); let binary = query_default_app(&attachment_type);
let mut name_opt = name.as_ref().and_then(|n| { let mut name_opt = name.as_ref().and_then(|n| {
melib::email::parser::phrase(n.as_bytes(), false) melib::email::parser::encodings::phrase(n.as_bytes(), false)
.to_full_result() .map(|(_, v)| v)
.ok() .ok()
.and_then(|n| String::from_utf8(n).ok()) .and_then(|n| String::from_utf8(n).ok())
}); });

View File

@ -117,38 +117,43 @@ impl MailBackend for PluginBackend {
}| { }| {
let mut env = melib::Envelope::new(hash); let mut env = melib::Envelope::new(hash);
env.set_date(date.as_bytes()); env.set_date(date.as_bytes());
if let Ok(d) = melib::email::parser::date(date.as_bytes()) { if let Ok(d) =
melib::email::parser::generic::date(date.as_bytes())
{
env.set_datetime(d); env.set_datetime(d);
} }
env.set_message_id(message_id.as_bytes()); env.set_message_id(message_id.as_bytes());
let parse_result = let parse_result =
melib::email::parser::rfc2822address_list( melib::email::parser::address::rfc2822address_list(
from.as_bytes(), from.as_bytes(),
); );
if parse_result.is_done() { if parse_result.is_ok() {
let value = parse_result.to_full_result().unwrap(); let value = parse_result.unwrap().1;
env.set_from(value); env.set_from(value);
} }
let parse_result = let parse_result =
melib::email::parser::rfc2822address_list( melib::email::parser::address::rfc2822address_list(
to.as_bytes(), to.as_bytes(),
); );
if parse_result.is_done() { if parse_result.is_ok() {
let value = parse_result.to_full_result().unwrap(); let value = parse_result.unwrap().1;
env.set_to(value); env.set_to(value);
} }
let parse_result = let parse_result = melib::email::parser::encodings::phrase(
melib::email::parser::phrase(subject.as_bytes(), false); subject.as_bytes(),
if parse_result.is_done() { false,
let value = parse_result.to_full_result().unwrap(); );
if parse_result.is_ok() {
let value = parse_result.unwrap().1;
env.set_subject(value); env.set_subject(value);
} }
if !references.is_empty() { if !references.is_empty() {
let parse_result = melib::email::parser::references( let parse_result =
references.as_bytes(), melib::email::parser::address::references(
); references.as_bytes(),
if parse_result.is_done() { );
for v in parse_result.to_full_result().unwrap() { if parse_result.is_ok() {
for v in parse_result.unwrap().1 {
env.push_references(v); env.push_references(v);
} }
} }