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"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arrayvec"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
dependencies = [
"nodrop",
]
[[package]]
name = "arrayvec"
version = "0.5.1"
@ -59,7 +68,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
dependencies = [
"arrayref",
"arrayvec",
"arrayvec 0.5.1",
"constant_time_eq",
]
@ -639,6 +648,19 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "libc"
version = "0.2.71"
@ -750,7 +772,7 @@ dependencies = [
"linkify",
"melib",
"nix",
"nom",
"nom 3.2.1",
"notify",
"notify-rust",
"pcre2",
@ -785,7 +807,7 @@ dependencies = [
"memmap",
"native-tls",
"nix",
"nom",
"nom 5.1.1",
"notify",
"notify-rust",
"reqwest",
@ -937,6 +959,12 @@ dependencies = [
"void",
]
[[package]]
name = "nodrop"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "nom"
version = "3.2.1"
@ -946,6 +974,17 @@ dependencies = [
"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]]
name = "notify"
version = "4.0.15"
@ -1339,6 +1378,15 @@ dependencies = [
"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]]
name = "ryu"
version = "1.0.4"
@ -1393,6 +1441,21 @@ dependencies = [
"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]]
name = "serde"
version = "1.0.110"
@ -1480,6 +1543,12 @@ dependencies = [
"serde",
]
[[package]]
name = "static_assertions"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
[[package]]
name = "syn"
version = "1.0.27"

View File

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

View File

@ -533,11 +533,11 @@ impl MailBackend for ImapType {
Ok(())
}
fn as_any(&self) -> &dyn ::std::any::Any {
fn as_any(&self) -> &dyn::std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any {
fn as_any_mut(&mut self) -> &mut dyn::std::any::Any {
self
}
@ -967,7 +967,7 @@ impl ImapType {
lines.next_back();
for l in lines.map(|l| l.trim()) {
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 mailboxes.contains_key(&parent) {
@ -1007,7 +1007,7 @@ impl ImapType {
lines.next_back();
for l in lines.map(|l| l.trim()) {
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 subscription.no_select {

View File

@ -195,8 +195,8 @@ impl ImapStream {
.ok_or_else(|| MeliError::new(""))
.and_then(|res| {
protocol_parser::capabilities(res.as_bytes())
.to_full_result()
.map_err(|_| MeliError::new(""))
.map(|(_, v)| v)
});
if capabilities.is_err() {
@ -241,8 +241,7 @@ impl ImapStream {
for l in res.split_rn() {
if l.starts_with("* CAPABILITY") {
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()))
})
.ok();
@ -269,7 +268,7 @@ impl ImapStream {
drop(capabilities);
ret.send_command(b"CAPABILITY")?;
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()));
Ok((capabilities, ret))
} else {

View File

@ -23,48 +23,53 @@ use super::{ImapConnection, ImapProtocol, ImapServerConf, UIDStore};
use crate::conf::AccountSettings;
use crate::error::{MeliError, Result};
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::sync::{Arc, Mutex};
use std::time::Instant;
named!(
pub managesieve_capabilities<Vec<(&[u8], &[u8])>>,
do_parse!(
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""[..]))))
>> opt!(tag!("\r\n"))
>> ({ ret })
)
);
pub fn managesieve_capabilities(input: &[u8]) -> Result<Vec<(&[u8], &[u8])>> {
let (_, ret) = separated_nonempty_list(
tag(b"\r\n"),
alt((
separated_pair(quoted_raw, tag(b" "), quoted_raw),
map(quoted_raw, |q| (q, &b""[..])),
)),
)(input)?;
Ok(ret)
}
#[test]
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![
(&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"NOTIFY"[..],&b"mailto"[..]),
(&b"SASL"[..],&b"PLAIN"[..]),
(&b"STARTTLS"[..], &b""[..]),
(&b"VERSION"[..],&b"1.0"[..])])
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"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"SASL"[..],&b"PLAIN"[..]),
(&b"STARTTLS"[..], &b""[..]),
(&b"VERSION"[..],&b"1.0"[..])]
);
);
}
// Return a byte sequence surrounded by "s and decoded if necessary
pub fn quoted_raw(input: &[u8]) -> IResult<&[u8], &[u8]> {
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;
while i < input.len() {
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;
}
return IResult::Error(nom::ErrorKind::Custom(0));
Err(nom::Err::Error((input, ErrorKind::Tag)))
}
pub trait ManageSieve {

View File

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

View File

@ -22,7 +22,16 @@
use super::*;
use crate::email::parser::BytesExt;
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;
bitflags! {
@ -277,7 +286,7 @@ macro_rules! to_str (
($v:expr) => (unsafe{ std::str::from_utf8_unchecked($v) })
);
macro_rules! dbg_dmp (
/*macro_rules! dbg_dmp (
($i: expr, $submac:ident!( $($args:tt)* )) => (
{
let l = line!();
@ -299,13 +308,59 @@ macro_rules! dbg_dmp (
dbg_dmp!($i, call!($f));
);
);
*/
/*
* LIST (\HasNoChildren) "." INBOX.Sent
* 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!(
ws!(alt_complete!(tag!("* LIST (") | tag!("* LSUB (")))
>> properties: take_until!(&b")"[0..])
@ -348,42 +403,74 @@ named!(
debug!(f)
})
)
);
*/
}
named!(
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)
});
pub fn my_flags(input: &[u8]) -> IResult<&[u8], Flag> {
let (input, flags) = separated_list(tag(" "), preceded(tag("\\"), is_not(")")))(input)?;
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)
});
}
}
}
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)]
pub struct UidFetchResponse<'a> {
@ -459,7 +546,10 @@ pub fn uid_fetch_response(input: &str) -> ImapParseResult<UidFetchResponse<'_>>
if input[i..].starts_with("UID ") {
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();
ret.uid = usize::from_str(unsafe { std::str::from_utf8_unchecked(uid) }).unwrap();
} else {
@ -470,7 +560,7 @@ pub fn uid_fetch_response(input: &str) -> ImapParseResult<UidFetchResponse<'_>>
}
} else if input[i..].starts_with("FLAGS (") {
i += "FLAGS (".len();
if let IResult::Done(rest, flags) = flags(&input[i..]) {
if let Ok((rest, flags)) = flags(&input[i..]) {
ret.flags = Some(flags);
i += (input.len() - i - rest.len()) + 1;
} else {
@ -481,16 +571,15 @@ pub fn uid_fetch_response(input: &str) -> ImapParseResult<UidFetchResponse<'_>>
}
} else if input[i..].starts_with("RFC822 {") {
i += "RFC822 ".len();
if let IResult::Done(rest, body) = length_bytes!(
input[i..].as_bytes(),
delimited!(
tag!("{"),
map_res!(digit, |s| {
if let Ok((rest, body)) =
length_data::<_, _, (&[u8], nom::error::ErrorKind), _>(delimited(
tag("{"),
map_res(digit1, |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);
i += input.len() - i - rest.len();
} else {
@ -501,7 +590,7 @@ pub fn uid_fetch_response(input: &str) -> ImapParseResult<UidFetchResponse<'_>>
}
} else if input[i..].starts_with("ENVELOPE (") {
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);
i += input.len() - i - rest.len();
} 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} "
*/
named!(
pub uid_fetch_response_<Vec<(usize, Option<(Flag, Vec<String>)>, &[u8])>>,
many0!(
do_parse!(
tag!("* ")
>> take_while!(call!(is_digit))
>> tag!(" FETCH (")
>> 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!(")")))),
ws!(length_bytes!(delimited!(tag!("{"), map_res!(digit, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) }), tag!("}\r\n")))))
>> tag!(")\r\n")
>> ((result.0, result.1, result.2))
)
)
);
pub fn uid_fetch_response_(
input: &[u8],
) -> IResult<&[u8], Vec<(usize, Option<(Flag, Vec<String>)>, &[u8])>> {
many0(
|input| -> IResult<&[u8], (usize, Option<(Flag, Vec<String>)>, &[u8])> {
let (input, _) = tag("* ")(input)?;
let (input, _) = take_while(is_digit)(input)?;
let (input, result) = permutation((
preceded(
tag("UID "),
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 uid_fetch_flags_response<Vec<(usize, (Flag, Vec<String>))>>,
many0!(
do_parse!(
tag!("* ")
>> take_while!(call!(is_digit))
>> tag!(" FETCH (")
>> 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!(")\r\n")
>> ((uid_flags.0, uid_flags.1))
pub fn uid_fetch_flags_response(input: &[u8]) -> IResult<&[u8], Vec<(usize, (Flag, Vec<String>))>> {
many0(|input| -> IResult<&[u8], (usize, (Flag, Vec<String>))> {
let (input, _) = tag("* ")(input)?;
let (input, _) = take_while(is_digit)(input)?;
let (input, _) = tag(" FETCH ( ")(input)?;
let (input, uid_flags) = permutation((
preceded(
tag("UID "),
map_res(digit1, |s| {
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 {
($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"
*/
named!(
pub capabilities<Vec<&[u8]>>,
pub fn capabilities(input: &[u8]) -> IResult<&[u8], 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!(
take_until!("CAPABILITY ")
>> tag!("CAPABILITY ")
@ -677,7 +803,8 @@ named!(
>> tag!("\r\n")
>> ({ ret })
)
);
*/
}
/// 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 {
@ -746,62 +873,130 @@ pub enum UntaggedResponse {
},
}
named!(
pub untagged_responses<Option<UntaggedResponse>>,
do_parse!(
tag!("* ")
>> num: map_res!(digit, |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)).to_full_result().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
}
pub fn untagged_responses(input: &[u8]) -> IResult<&[u8], Option<UntaggedResponse>> {
let (input, _) = tag("* ")(input)?;
let (input, num) = map_res(digit1, |s| {
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
})(input)?;
let (input, _) = tag(" ")(input)?;
let (input, _tag) = take_until("\r\n")(input)?;
let (input, _) = tag("\r\n")(input)?;
Ok((input, {
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) },
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 search_results<Vec<usize>>,
pub fn search_results<'a>(input: &'a [u8]) -> IResult<&'a [u8], 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 ")
>> 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")
>> ({ list })) |
do_parse!(tag!("* SEARCH\r\n") >> ({ Vec::new() })))
);
*/
}
named!(
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..] })))
);
pub fn search_results_raw<'a>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8]> {
alt((
|input: &'a [u8]| -> IResult<&'a [u8], &'a [u8]> {
let (input, _) = tag("* SEARCH ")(input)?;
let (input, list) = take_until("\r\n")(input)?;
let (input, _) = tag("\r\n")(input)?;
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]
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!(
search_results(b"* SEARCH 1\r\n").to_full_result(),
search_results(b"* SEARCH 1\r\n").map(|(_, v)| v),
Ok(vec![1])
);
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])
);
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"[..])
);
}
@ -851,7 +1046,7 @@ pub fn select_response(input: &str) -> Result<SelectResponse> {
} else if l.starts_with("* ") && l.ends_with(" RECENT") {
ret.recent = usize::from_str(&l["* ".len()..l.len() - " RECENT".len()])?;
} 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 ") {
ret.unseen = usize::from_str(&l["* OK [UNSEEN ".len()..l.find(']').unwrap()])?;
} 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 (") {
ret.permanentflags =
flags(&l["* OK [PERMANENTFLAGS (".len()..l.find(')').unwrap()])
.to_full_result()?;
.map(|(_, v)| v)?;
ret.can_create_flags = l.contains("\\*");
} else if l.contains("OK [READ-WRITE]") {
ret.read_only = false;
@ -919,15 +1114,16 @@ pub fn flags(input: &str) -> IResult<&str, (Flag, Vec<String>)> {
input = &input[match_end..];
input = input.trim_start();
}
IResult::Done(input, (ret, keywords))
Ok((input, (ret, keywords)))
}
pub fn byte_flags(input: &[u8]) -> IResult<&[u8], (Flag, Vec<String>)> {
let i = unsafe { std::str::from_utf8_unchecked(input) };
match flags(i) {
IResult::Done(rest, ret) => IResult::Done(rest.as_bytes(), ret),
IResult::Error(e) => IResult::Error(e),
IResult::Incomplete(e) => IResult::Incomplete(e),
Ok((rest, ret)) => Ok((rest.as_bytes(), ret)),
Err(nom::Err::Error((_, err))) => Err(nom::Err::Error((input, err))),
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>")
*/
named!(
pub envelope<Envelope>,
pub fn envelope(input: &[u8]) -> IResult<&[u8], 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!(
tag!("(")
>> opt!(is_a!("\r\n\t "))
@ -987,7 +1246,7 @@ named!(
let mut env = Envelope::new(0);
if let Some(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);
}
}
@ -1020,7 +1279,8 @@ named!(
}
env
})
));
*/
}
/* Helper to build StrBuilder for Address structs */
macro_rules! str_builder {
@ -1033,94 +1293,179 @@ macro_rules! str_builder {
}
// Parse a list of addresses in the format of the ENVELOPE structure
named!(pub envelope_addresses<Option<Vec<Address>>>,
alt_complete!(map!(tag!("NIL"), |_| None) |
do_parse!(
tag!("(")
>> envelopes: many1!(delimited!(ws!(tag!("(")), envelope_address, tag!(")")))
>> tag!(")")
>> ({
Some(envelopes)
})
)
));
pub fn envelope_addresses<'a>(input: &'a [u8]) -> IResult<&'a [u8], Option<Vec<Address>>> {
alt((
map(tag("NIL"), |_| None),
|input: &'a [u8]| -> IResult<&'a [u8], Option<Vec<Address>>> {
let (input, _) = tag("(")(input)?;
let (input, envelopes) =
many1(delimited(tag("("), envelope_address, tag(")")))(input.ltrim())?;
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
// ("Terry Gray" NIL "gray" "cac.washington.edu")
named!(
pub envelope_address<Address>,
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()))
>> ({
pub fn envelope_address(input: &[u8]) -> IResult<&[u8], Address> {
let (input, name) = alt((quoted, map(tag("NIL"), |_| Vec::new())))(input)?;
let (input, _) = is_a("\r\n\t ")(input)?;
let (input, _) = alt((quoted, map(tag("NIL"), |_| Vec::new())))(input)?;
let (input, _) = is_a("\r\n\t ")(input)?;
let (input, mailbox_name) = alt((quoted, map(tag("NIL"), |_| Vec::new())))(input)?;
let (input, _) = is_a("\r\n\t ")(input)?;
let (input, host_name) = alt((quoted, map(tag("NIL"), |_| Vec::new())))(input)?;
Ok((
input,
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()),
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
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
pub fn quoted(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
if let IResult::Done(r, o) = literal(input) {
return match crate::email::parser::phrase(o, false) {
IResult::Done(_, out) => IResult::Done(r, out),
if let Ok((r, o)) = literal(input) {
return match crate::email::parser::encodings::phrase(o, false) {
Ok((_, out)) => Ok((r, out)),
e => e,
};
}
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;
while i < input.len() {
if input[i] == b'\"' && input[i - 1] != b'\\' {
return match crate::email::parser::phrase(&input[1..i], false) {
IResult::Done(_, out) => IResult::Done(&input[i + 1..], out),
return match crate::email::parser::encodings::phrase(&input[1..i], false) {
Ok((_, out)) => Ok((&input[i + 1..], out)),
e => e,
};
}
i += 1;
}
return IResult::Error(nom::ErrorKind::Custom(0));
return Err(nom::Err::Error((input, nom::error::ErrorKind::Tag)));
}
named!(
pub quoted_or_nil<Option<Vec<u8>>>,
pub fn quoted_or_nil(input: &[u8]) -> IResult<&[u8], 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))));
*/
}
named!(
pub uid_fetch_envelopes_response<Vec<(usize, Option<(Flag, Vec<String>)>, Envelope)>>,
many0!(
do_parse!(
tag!("* ")
>> take_while!(call!(is_digit))
>> tag!(" FETCH (")
>> 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!(")")))))
>> tag!(" ENVELOPE ")
>> env: ws!(envelope)
>> tag!("BODYSTRUCTURE ")
>> bodystructure: take_until!(")\r\n")
>> tag!(")\r\n")
>> ({
pub fn uid_fetch_envelopes_response(
input: &[u8],
) -> IResult<&[u8], Vec<(usize, Option<(Flag, Vec<String>)>, Envelope)>> {
many0(
|input: &[u8]| -> IResult<&[u8], (usize, Option<(Flag, Vec<String>)>, Envelope)> {
let (input, _) = tag("* ")(input)?;
let (input, _) = take_while(is_digit)(input)?;
let (input, _) = tag(" FETCH (")(input)?;
let (input, uid_flags) = permutation((
preceded(
tag("UID "),
map_res(digit1, |s| {
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 has_attachments = bodystructure_has_attachments(bodystructure);
env.set_has_attachments(has_attachments);
(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 {
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 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(_) => {
return Ok(false);
}
@ -162,7 +162,7 @@ impl ImapConnection {
self.read_response(&mut response, RequiredResponses::SEARCH)
);
match super::protocol_parser::search_results_raw(response.as_bytes())
.to_full_result()
.map(|(_, v)| v)
.map_err(MeliError::from)
{
Ok(&[]) => {
@ -267,7 +267,7 @@ impl ImapConnection {
match super::protocol_parser::search_results(
response.split_rn().next().unwrap_or("").as_bytes(),
)
.to_full_result()
.map(|(_, v)| v)
{
Ok(mut v) => {
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(()));
match protocol_parser::untagged_responses(line.as_slice())
.to_full_result()
.map(|(_, v)| v)
.map_err(MeliError::from)
{
Ok(Some(Recent(r))) => {
@ -292,7 +292,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
conn.read_response(&mut response, RequiredResponses::SEARCH)
);
match protocol_parser::search_results_raw(response.as_bytes())
.to_full_result()
.map(|(_, v)| v)
.map_err(MeliError::from)
{
Ok(&[]) => {
@ -529,7 +529,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
conn.read_response(&mut response, RequiredResponses::SEARCH)
);
match search_results(response.split_rn().next().unwrap_or("").as_bytes())
.to_full_result()
.map(|(_, v)| v)
{
Ok(mut v) => {
if let Some(uid) = v.pop() {
@ -646,7 +646,7 @@ pub fn examine_updates(
conn.read_response(&mut response, RequiredResponses::SEARCH)
);
match protocol_parser::search_results_raw(response.as_bytes())
.to_full_result()
.map(|(_, v)| v)
.map_err(MeliError::from)
{
Ok(&[]) => {

View File

@ -231,7 +231,7 @@ impl std::fmt::Display for EmailAddress {
impl std::convert::From<EmailObject> for crate::Envelope {
fn from(mut t: EmailObject) -> crate::Envelope {
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);
}
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());
}
if let Some(v) = t.headers.get("References") {
let parse_result = crate::email::parser::references(v.as_bytes());
if parse_result.is_done() {
for v in parse_result.to_full_result().unwrap() {
let parse_result = crate::email::parser::address::references(v.as_bytes());
if let Ok((_, v)) = parse_result {
for v in 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") {
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);
}
}

View File

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

View File

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

View File

@ -218,7 +218,7 @@ impl StrBuild for MessageID {
#[test]
fn test_strbuilder() {
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!(
MessageID::new(m_id, slice),
MessageID(

View File

@ -39,8 +39,8 @@ pub struct AttachmentBuilder {
impl AttachmentBuilder {
pub fn new(content: &[u8]) -> Self {
let (headers, body) = match parser::attachment(content).to_full_result() {
Ok(v) => v,
let (headers, body) = match parser::attachments::attachment(content) {
Ok((_, v)) => v,
Err(_) => {
debug!("error in parsing attachment");
debug!("\n-------------------------------");
@ -121,8 +121,8 @@ impl AttachmentBuilder {
}
pub fn set_content_type_from_bytes(&mut self, value: &[u8]) -> &mut Self {
match parser::content_type(value).to_full_result() {
Ok((ct, cst, params)) => {
match parser::attachments::content_type(value) {
Ok((_, (ct, cst, params))) => {
if ct.eq_ignore_ascii_case(b"multipart") {
let mut boundary = None;
for (n, v) in params {
@ -185,10 +185,9 @@ impl AttachmentBuilder {
let mut name: Option<String> = None;
for (n, v) in params {
if n.eq_ignore_ascii_case(b"name") {
if let Ok(v) = crate::email::parser::phrase(v.trim(), false)
.to_full_result()
if let Ok(v) = crate::email::parser::encodings::phrase(v.trim(), false)
.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);
} else {
@ -229,13 +228,13 @@ impl AttachmentBuilder {
return Vec::new();
}
match parser::parts(raw, boundary).to_full_result() {
Ok(attachments) => {
match parser::attachments::parts(raw, boundary) {
Ok((_, attachments)) => {
let mut vec = Vec::with_capacity(attachments.len());
for a in attachments {
let mut builder = AttachmentBuilder::default();
let (headers, body) = match parser::attachment(&a).to_full_result() {
Ok(v) => v,
let (headers, body) = match parser::attachments::attachment(&a) {
Ok((_, v)) => v,
Err(_) => {
debug!("error in parsing attachment");
debug!("\n-------------------------------");
@ -420,8 +419,8 @@ impl Attachment {
match self.content_type {
ContentType::Multipart { ref boundary, .. } => {
match parser::multipart_parts(self.body(), boundary).to_full_result() {
Ok(v) => v,
match parser::attachments::multipart_parts(self.body(), boundary) {
Ok((_, v)) => v,
Err(e) => {
debug!("error in parsing attachment");
debug!("\n-------------------------------");
@ -444,10 +443,12 @@ impl Attachment {
}
// FIXME: check if any part is multipart/mixed as well
match parser::multipart_parts(bytes, boundary).to_full_result() {
Ok(parts) => {
match parser::attachments::multipart_parts(bytes, boundary) {
Ok((_, 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/") {
return true;
}
@ -655,14 +656,14 @@ impl Attachment {
pub fn parameters(&self) -> Vec<(&[u8], &[u8])> {
let mut ret = Vec::new();
let (headers, _) = match parser::attachment(&self.raw).to_full_result() {
Ok(v) => v,
let (headers, _) = match parser::attachments::attachment(&self.raw) {
Ok((_, v)) => v,
Err(_) => return ret,
};
for (name, value) in headers {
if name.eq_ignore_ascii_case(b"content-type") {
match parser::content_type(value).to_full_result() {
Ok((_, _, params)) => {
match parser::attachments::content_type(value) {
Ok((_, (_, _, params))) => {
ret = params;
}
_ => {}
@ -751,16 +752,18 @@ fn decode_helper<'a>(a: &'a Attachment, filter: &mut Option<Filter<'a>>) -> Vec<
Ok(v) => v,
_ => a.body().to_vec(),
},
ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_bytes(a.body())
.to_full_result()
.unwrap(),
ContentTransferEncoding::QuotedPrintable => {
parser::encodings::quoted_printable_bytes(a.body())
.unwrap()
.1
}
ContentTransferEncoding::_7Bit
| ContentTransferEncoding::_8Bit
| ContentTransferEncoding::Other { .. } => a.body().to_vec(),
};
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()
} else {
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"));
}
let (headers, _) = parser::mail(s.as_bytes()).to_full_result()?;
let (headers, _) = parser::mail(s.as_bytes())?;
let mut ret = Draft::default();
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 let super::parser::IResult::Done(_, addr) =
super::parser::mailbox(ret.headers["From"].as_bytes())
{
if let Ok((_, addr)) = super::parser::address::mailbox(ret.headers["From"].as_bytes()) {
if let Some(fqdn) = addr.get_fqdn() {
if ret
.headers
@ -256,8 +254,7 @@ impl Draft {
let mut ret = String::new();
if self.headers.contains_key("From") && !self.headers.contains_key("Message-ID") {
if let super::parser::IResult::Done(_, addr) =
super::parser::mailbox(self.headers["From"].as_bytes())
if let Ok((_, addr)) = super::parser::address::mailbox(self.headers["From"].as_bytes())
{
if let Some(fqdn) = addr.get_fqdn() {
if self

View File

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

View File

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

View File

@ -54,7 +54,7 @@ impl TryFrom<&[u8]> for Mailto {
type Error = String;
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() {
Ok(parse_res.unwrap())
} else {
@ -74,8 +74,8 @@ mod tests {
#[test]
fn test_mailto() {
let test_address = super::parser::address(b"info@example.com")
.to_full_result()
let test_address = super::parser::address::address(b"info@example.com")
.map(|(_, v)| v)
.unwrap();
let mailto = Mailto::try_from(&b"mailto:info@example.com?subject=email%20subject"[0..])
.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::sync::Arc;
use nom;
pub type Result<T> = result::Result<T, MeliError>;
#[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 {
#[inline]
fn from(kind: Cow<'_, str>) -> MeliError {
@ -261,3 +252,19 @@ impl From<String> for MeliError {
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]
extern crate serde_derive;
/* parser */
#[macro_use]
extern crate nom;
extern crate data_encoding;
extern crate encoding;
pub use nom;
#[macro_use]
extern crate bitflags;

View File

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

View File

@ -701,17 +701,16 @@ impl Component for MailView {
let mut ret = op
.as_bytes()
.and_then(|b| {
melib::email::parser::headers(b)
.to_full_result()
melib::email::parser::headers::headers(b)
.map(|(_, v)| v)
.map_err(|err| err.into())
})
.and_then(|headers| {
Ok(headers
.into_iter()
.map(|(h, v)| {
melib::email::parser::phrase(v, true)
.to_full_result()
.map(|v| {
melib::email::parser::encodings::phrase(v, true)
.map(|(_, v)| {
let mut h = h.to_vec();
h.push(b':');
h.push(b' ');
@ -1106,8 +1105,8 @@ impl Component for MailView {
let attachment_type = u.mime_type();
let binary = query_default_app(&attachment_type);
let mut name_opt = name.as_ref().and_then(|n| {
melib::email::parser::phrase(n.as_bytes(), false)
.to_full_result()
melib::email::parser::encodings::phrase(n.as_bytes(), false)
.map(|(_, v)| v)
.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);
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_message_id(message_id.as_bytes());
let parse_result =
melib::email::parser::rfc2822address_list(
melib::email::parser::address::rfc2822address_list(
from.as_bytes(),
);
if parse_result.is_done() {
let value = parse_result.to_full_result().unwrap();
if parse_result.is_ok() {
let value = parse_result.unwrap().1;
env.set_from(value);
}
let parse_result =
melib::email::parser::rfc2822address_list(
melib::email::parser::address::rfc2822address_list(
to.as_bytes(),
);
if parse_result.is_done() {
let value = parse_result.to_full_result().unwrap();
if parse_result.is_ok() {
let value = parse_result.unwrap().1;
env.set_to(value);
}
let parse_result =
melib::email::parser::phrase(subject.as_bytes(), false);
if parse_result.is_done() {
let value = parse_result.to_full_result().unwrap();
let parse_result = melib::email::parser::encodings::phrase(
subject.as_bytes(),
false,
);
if parse_result.is_ok() {
let value = parse_result.unwrap().1;
env.set_subject(value);
}
if !references.is_empty() {
let parse_result = melib::email::parser::references(
references.as_bytes(),
);
if parse_result.is_done() {
for v in parse_result.to_full_result().unwrap() {
let parse_result =
melib::email::parser::address::references(
references.as_bytes(),
);
if parse_result.is_ok() {
for v in parse_result.unwrap().1 {
env.push_references(v);
}
}