parent
db4c401828
commit
6ec249dd7f
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,9 +403,40 @@ named!(
|
|||
debug!(f)
|
||||
})
|
||||
)
|
||||
);
|
||||
*/
|
||||
|
||||
named!(
|
||||
}
|
||||
|
||||
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!(")")))
|
||||
|
@ -383,7 +469,8 @@ named!(
|
|||
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!(")"))))
|
||||
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,11 +873,45 @@ pub enum UntaggedResponse {
|
|||
},
|
||||
}
|
||||
|
||||
named!(
|
||||
pub untagged_responses<Option<UntaggedResponse>>,
|
||||
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!(digit, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) })
|
||||
>> num: map_res!(digit1, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) })
|
||||
>> tag!(" ")
|
||||
>> tag: take_until!("\r\n")
|
||||
>> tag!("\r\n")
|
||||
|
@ -761,7 +922,7 @@ named!(
|
|||
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(),
|
||||
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
|
||||
|
@ -769,39 +930,73 @@ unsafe { std::str::from_utf8_unchecked(&tag[b"FETCH (FLAGS (".len()..]) }).map(|
|
|||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
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 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,7 +1293,18 @@ macro_rules! str_builder {
|
|||
}
|
||||
|
||||
// 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((
|
||||
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!("(")
|
||||
|
@ -1043,12 +1314,39 @@ named!(pub envelope_addresses<Option<Vec<Address>>>,
|
|||
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>,
|
||||
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(),
|
||||
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
|
||||
),
|
||||
}),
|
||||
))
|
||||
/*
|
||||
do_parse!(
|
||||
name: alt_complete!(quoted | map!(tag!("NIL"), |_| Vec::new()))
|
||||
>> is_a!("\r\n\t ")
|
||||
|
@ -1064,49 +1362,94 @@ named!(
|
|||
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)>>,
|
||||
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!(digit, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) })), opt!(preceded!(ws!(tag!("FLAGS ")), delimited!(tag!("("), byte_flags, tag!(")")))))
|
||||
>> 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 ")
|
||||
|
@ -1120,7 +1463,9 @@ named!(
|
|||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
pub fn bodystructure_has_attachments(input: &[u8]) -> bool {
|
||||
input.rfind(b" \"mixed\" ").is_some() || input.rfind(b" \"MIXED\" ").is_some()
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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(&[]) => {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
);
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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)))))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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())
|
||||
});
|
||||
|
|
|
@ -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(
|
||||
let parse_result =
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue