imap: add mailbox_token() parser
parent
c2300e8ea0
commit
c7835ccc13
|
@ -346,7 +346,7 @@ pub fn list_mailbox_result(input: &[u8]) -> IResult<&[u8], ImapMailbox> {
|
||||||
let (input, _) = tag(b") ")(input)?;
|
let (input, _) = tag(b") ")(input)?;
|
||||||
let (input, separator) = delimited(tag(b"\""), take(1_u32), tag(b"\""))(input)?;
|
let (input, separator) = delimited(tag(b"\""), take(1_u32), tag(b"\""))(input)?;
|
||||||
let (input, _) = take(1_u32)(input)?;
|
let (input, _) = take(1_u32)(input)?;
|
||||||
let (input, path) = alt((delimited(tag("\""), is_not("\""), tag("\"")), rest))(input)?;
|
let (input, path) = mailbox_token(input)?;
|
||||||
Ok((
|
Ok((
|
||||||
input,
|
input,
|
||||||
({
|
({
|
||||||
|
@ -366,7 +366,8 @@ pub fn list_mailbox_result(input: &[u8]) -> IResult<&[u8], ImapMailbox> {
|
||||||
let _ = f.set_special_usage(SpecialUsageMailbox::Drafts);
|
let _ = f.set_special_usage(SpecialUsageMailbox::Drafts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.imap_path = String::from_utf8_lossy(path).into();
|
f.is_subscribed = path == "INBOX";
|
||||||
|
f.imap_path = path.into();
|
||||||
f.hash = get_path_hash!(&f.imap_path);
|
f.hash = get_path_hash!(&f.imap_path);
|
||||||
f.path = if separator == b'/' {
|
f.path = if separator == b'/' {
|
||||||
f.imap_path.clone()
|
f.imap_path.clone()
|
||||||
|
@ -385,51 +386,6 @@ pub fn list_mailbox_result(input: &[u8]) -> IResult<&[u8], ImapMailbox> {
|
||||||
debug!(f)
|
debug!(f)
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
/*
|
|
||||||
do_parse!(
|
|
||||||
ws!(alt_complete!(tag!("* LIST (") | tag!("* LSUB (")))
|
|
||||||
>> properties: take_until!(&b")"[0..])
|
|
||||||
>> tag!(b") ")
|
|
||||||
>> separator: delimited!(tag!(b"\""), take!(1), tag!(b"\""))
|
|
||||||
>> take!(1)
|
|
||||||
>> path: alt_complete!(delimited!(tag!("\""), is_not!("\""), tag!("\"")) | call!(rest))
|
|
||||||
>> ({
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn my_flags(input: &[u8]) -> IResult<&[u8], Flag> {
|
pub fn my_flags(input: &[u8]) -> IResult<&[u8], Flag> {
|
||||||
|
@ -460,41 +416,6 @@ pub fn my_flags(input: &[u8]) -> IResult<&[u8], Flag> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((input, ret))
|
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
|
|
||||||
})
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -763,23 +684,13 @@ pub fn uid_fetch_flags_response(input: &[u8]) -> IResult<&[u8], Vec<(usize, (Fla
|
||||||
let (input, uid_flags) = permutation((
|
let (input, uid_flags) = permutation((
|
||||||
preceded(
|
preceded(
|
||||||
tag("UID "),
|
tag("UID "),
|
||||||
map_res(digit1, |s| {
|
map_res(digit1, |s| usize::from_str(to_str!(s))),
|
||||||
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
preceded(tag("FLAGS "), delimited(tag("("), byte_flags, tag(")"))),
|
preceded(tag("FLAGS "), delimited(tag("("), byte_flags, tag(")"))),
|
||||||
))(input.ltrim())?;
|
))(input.ltrim())?;
|
||||||
let (input, _) = tag(")\r\n")(input)?;
|
let (input, _) = tag(")\r\n")(input)?;
|
||||||
Ok((input, (uid_flags.0, uid_flags.1)))
|
Ok((input, (uid_flags.0, uid_flags.1)))
|
||||||
})(input)
|
})(input)
|
||||||
|
|
||||||
/*
|
|
||||||
>> uid_flags: permutation!(preceded!(ws!(tag!("UID ")), map_res!(digit1, |s| { usize::from_str(unsafe { std::str::from_utf8_unchecked(s) }) })), preceded!(ws!(tag!("FLAGS ")), delimited!(tag!("("), byte_flags, tag!(")"))))
|
|
||||||
>> tag!(")\r\n")
|
|
||||||
>> ((uid_flags.0, uid_flags.1))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! flags_to_imap_list {
|
macro_rules! flags_to_imap_list {
|
||||||
|
@ -913,9 +824,7 @@ pub enum UntaggedResponse {
|
||||||
|
|
||||||
pub fn untagged_responses(input: &[u8]) -> IResult<&[u8], Option<UntaggedResponse>> {
|
pub fn untagged_responses(input: &[u8]) -> IResult<&[u8], Option<UntaggedResponse>> {
|
||||||
let (input, _) = tag("* ")(input)?;
|
let (input, _) = tag("* ")(input)?;
|
||||||
let (input, num) = map_res(digit1, |s| {
|
let (input, num) = map_res(digit1, |s| usize::from_str(to_str!(s)))(input)?;
|
||||||
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
|
|
||||||
})(input)?;
|
|
||||||
let (input, _) = tag(" ")(input)?;
|
let (input, _) = tag(" ")(input)?;
|
||||||
let (input, _tag) = take_until("\r\n")(input)?;
|
let (input, _tag) = take_until("\r\n")(input)?;
|
||||||
let (input, _) = tag("\r\n")(input)?;
|
let (input, _) = tag("\r\n")(input)?;
|
||||||
|
@ -926,49 +835,25 @@ pub fn untagged_responses(input: &[u8]) -> IResult<&[u8], Option<UntaggedRespons
|
||||||
b"EXISTS" => Some(Exists(num)),
|
b"EXISTS" => Some(Exists(num)),
|
||||||
b"RECENT" => Some(Recent(num)),
|
b"RECENT" => Some(Recent(num)),
|
||||||
_ if _tag.starts_with(b"FETCH ") => {
|
_ if _tag.starts_with(b"FETCH ") => {
|
||||||
flags(unsafe { std::str::from_utf8_unchecked(&_tag[b"FETCH (FLAGS (".len()..]) })
|
let f = flags(unsafe {
|
||||||
.map(|(_, flags)| Fetch(num, flags))
|
std::str::from_utf8_unchecked(&_tag[b"FETCH (FLAGS (".len()..])
|
||||||
.map_err(|err| {
|
})
|
||||||
debug!(
|
.map(|(_, flags)| Fetch(num, flags));
|
||||||
"untagged_response malformed fetch: {} {}",
|
if let Err(ref err) = f {
|
||||||
unsafe { std::str::from_utf8_unchecked(_tag) },
|
debug!(
|
||||||
err
|
"untagged_response malformed fetch: {} {}",
|
||||||
)
|
unsafe { std::str::from_utf8_unchecked(_tag) },
|
||||||
})
|
err
|
||||||
.ok()
|
)
|
||||||
|
}
|
||||||
|
f.ok()
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
debug!("unknown untagged_response: {}", unsafe {
|
debug!("unknown untagged_response: {}", to_str!(_tag));
|
||||||
std::str::from_utf8_unchecked(_tag)
|
|
||||||
});
|
|
||||||
None
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_results<'a>(input: &'a [u8]) -> IResult<&'a [u8], Vec<usize>> {
|
pub fn search_results<'a>(input: &'a [u8]) -> IResult<&'a [u8], Vec<usize>> {
|
||||||
|
@ -989,13 +874,6 @@ pub fn search_results<'a>(input: &'a [u8]) -> IResult<&'a [u8], Vec<usize>> {
|
||||||
Ok((input, vec![]))
|
Ok((input, vec![]))
|
||||||
},
|
},
|
||||||
))(input)
|
))(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() })))
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_results_raw<'a>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8]> {
|
pub fn search_results_raw<'a>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8]> {
|
||||||
|
@ -1011,15 +889,6 @@ pub fn search_results_raw<'a>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8]> {
|
||||||
Ok((input, &b""[0..]))
|
Ok((input, &b""[0..]))
|
||||||
},
|
},
|
||||||
))(input)
|
))(input)
|
||||||
/*
|
|
||||||
pub search_results_raw<&[u8]>,
|
|
||||||
alt_complete!(do_parse!( tag!("* SEARCH ")
|
|
||||||
>> list: take_until!("\r\n")
|
|
||||||
>> tag!("\r\n")
|
|
||||||
>> ({ list })) |
|
|
||||||
do_parse!(tag!("* SEARCH\r\n") >> ({ &b""[0..] })))
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1532,3 +1401,55 @@ pub fn uid_fetch_envelopes_response(
|
||||||
pub fn bodystructure_has_attachments(input: &[u8]) -> bool {
|
pub fn bodystructure_has_attachments(input: &[u8]) -> bool {
|
||||||
input.rfind(b" \"mixed\" ").is_some() || input.rfind(b" \"MIXED\" ").is_some()
|
input.rfind(b" \"mixed\" ").is_some() || input.rfind(b" \"MIXED\" ").is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mailbox = "INBOX" / astring
|
||||||
|
// ; INBOX is case-insensitive. All case variants of
|
||||||
|
// ; INBOX (e.g., "iNbOx") MUST be interpreted as INBOX
|
||||||
|
// ; not as an astring. An astring which consists of
|
||||||
|
// ; the case-insensitive sequence "I" "N" "B" "O" "X"
|
||||||
|
// ; is considered to be INBOX and not an astring.
|
||||||
|
// ; Refer to section 5.1 for further
|
||||||
|
// ; semantic details of mailbox names.
|
||||||
|
pub fn mailbox_token<'i>(input: &'i [u8]) -> IResult<&'i [u8], &'i str> {
|
||||||
|
let (input, astring) = astring_token(input)?;
|
||||||
|
if astring.eq_ignore_ascii_case(b"INBOX") {
|
||||||
|
return Ok((input, "INBOX"));
|
||||||
|
}
|
||||||
|
Ok((input, to_str!(astring)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// astring = 1*ASTRING-CHAR / string
|
||||||
|
fn astring_token(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||||
|
alt((string_token, astring_char_tokens))(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// string = quoted / literal
|
||||||
|
fn string_token(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||||
|
if let Ok((r, o)) = literal(input) {
|
||||||
|
return Ok((r, o));
|
||||||
|
}
|
||||||
|
if input.is_empty() || input[0] != b'"' {
|
||||||
|
return Err(nom::Err::Error((input, "string_token(): EOF").into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut i = 1;
|
||||||
|
while i < input.len() {
|
||||||
|
if input[i] == b'\"' && input[i - 1] != b'\\' {
|
||||||
|
return Ok((&input[i + 1..], &input[1..i]));
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(nom::Err::Error(
|
||||||
|
(input, "string_token(): not a quoted phrase").into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASTRING-CHAR = ATOM-CHAR / resp-specials
|
||||||
|
// atom = 1*ATOM-CHAR
|
||||||
|
// ATOM-CHAR = <any CHAR except atom-specials>
|
||||||
|
// atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / quoted-specials / resp-specials
|
||||||
|
fn astring_char_tokens(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||||
|
// FIXME
|
||||||
|
is_not(" ")(input)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue