imap: add mailbox_token() parser

memfd
Manos Pitsidianakis 2020-06-23 12:31:40 +03:00
parent c2300e8ea0
commit c7835ccc13
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
1 changed files with 70 additions and 149 deletions

View File

@ -346,7 +346,7 @@ pub fn list_mailbox_result(input: &[u8]) -> IResult<&[u8], ImapMailbox> {
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)?;
let (input, path) = mailbox_token(input)?;
Ok((
input,
({
@ -366,7 +366,8 @@ pub fn list_mailbox_result(input: &[u8]) -> IResult<&[u8], ImapMailbox> {
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.path = if separator == b'/' {
f.imap_path.clone()
@ -385,51 +386,6 @@ pub fn list_mailbox_result(input: &[u8]) -> IResult<&[u8], ImapMailbox> {
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> {
@ -460,41 +416,6 @@ pub fn my_flags(input: &[u8]) -> IResult<&[u8], Flag> {
}
}
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)]
@ -763,23 +684,13 @@ pub fn uid_fetch_flags_response(input: &[u8]) -> IResult<&[u8], Vec<(usize, (Fla
let (input, uid_flags) = permutation((
preceded(
tag("UID "),
map_res(digit1, |s| {
usize::from_str(unsafe { std::str::from_utf8_unchecked(s) })
}),
map_res(digit1, |s| usize::from_str(to_str!(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 {
@ -913,9 +824,7 @@ pub enum 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, num) = map_res(digit1, |s| usize::from_str(to_str!(s)))(input)?;
let (input, _) = tag(" ")(input)?;
let (input, _tag) = take_until("\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"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()
let f = flags(unsafe {
std::str::from_utf8_unchecked(&_tag[b"FETCH (FLAGS (".len()..])
})
.map(|(_, flags)| Fetch(num, flags));
if let Err(ref err) = f {
debug!(
"untagged_response malformed fetch: {} {}",
unsafe { std::str::from_utf8_unchecked(_tag) },
err
)
}
f.ok()
}
_ => {
debug!("unknown untagged_response: {}", unsafe {
std::str::from_utf8_unchecked(_tag)
});
debug!("unknown untagged_response: {}", to_str!(_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
}
}
})
)
*/
}
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![]))
},
))(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]> {
@ -1011,15 +889,6 @@ pub fn search_results_raw<'a>(input: &'a [u8]) -> IResult<&'a [u8], &'a [u8]> {
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]
@ -1532,3 +1401,55 @@ pub fn uid_fetch_envelopes_response(
pub fn bodystructure_has_attachments(input: &[u8]) -> bool {
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)
}