Browse Source

imap: add mailbox_token() parser

Manos Pitsidianakis 5 years ago
parent
commit
c7835ccc13
1 changed files with 70 additions and 149 deletions
  1. 70 149
      melib/src/backends/imap/protocol_parser.rs

+ 70 - 149
melib/src/backends/imap/protocol_parser.rs

@@ -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)
+}