From 7f7a4a115a2c39bfb85a4b2e64e0c3532af17f0c Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Tue, 2 Mar 2021 16:48:27 -0600 Subject: [PATCH] woops gotta clone outside the loop --- imap/src/parser/old.rs | 531 ----------------------------------------- src/mail/mod.rs | 1 + 2 files changed, 1 insertion(+), 531 deletions(-) delete mode 100644 imap/src/parser/old.rs diff --git a/imap/src/parser/old.rs b/imap/src/parser/old.rs deleted file mode 100644 index fb0d6f5..0000000 --- a/imap/src/parser/old.rs +++ /dev/null @@ -1,531 +0,0 @@ -//! Module that implements parsers for all of the IMAP types. - -mod literal; -mod old; - -use std::fmt::Debug; -use std::mem; -use std::str::FromStr; - -use pest::{error::Error, iterators::Pair, ParseResult as PestResult, Parser, ParserState}; - -use crate::response::*; - -#[derive(Parser)] -#[grammar = "parser/rfc3501.pest"] - -struct Rfc3501; - -pub type ParseResult> = Result; - -pub fn parse_capability(s: impl AsRef) -> ParseResult { - let mut pairs = Rfc3501::parse(Rule::capability, s.as_ref())?; - let pair = pairs.next().unwrap(); - Ok(build_capability(pair)) -} - -pub fn parse_response(s: impl AsRef) -> ParseResult { - let mut pairs = Rfc3501::parse(Rule::response, s.as_ref())?; - let pair = pairs.next().unwrap(); - Ok(build_response(pair)) -} - -fn build_response(pair: Pair) -> Response { - assert!(matches!(pair.as_rule(), Rule::response)); - - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - match pair.as_rule() { - Rule::response_done => { - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - match pair.as_rule() { - Rule::response_tagged => { - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - let tag = pair.as_str().to_owned(); - - let pair = pairs.next().unwrap(); - let (status, code, information) = build_resp_cond_state(pair); - Response::Done { - tag, - status, - code, - information, - } - } - _ => unreachable!("{:#?}", pair), - } - } - Rule::response_data => { - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - match pair.as_rule() { - Rule::resp_cond_state => { - let (status, code, information) = build_resp_cond_state(pair); - Response::Data { - status, - code, - information, - } - } - Rule::mailbox_data => Response::MailboxData(build_mailbox_data(pair)), - Rule::capability_data => Response::Capabilities(build_capabilities(pair)), - Rule::message_data => { - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - let seq: u32 = build_number(pair); - - let pair = pairs.next().unwrap(); - match pair.as_rule() { - Rule::message_data_expunge => Response::Expunge(seq), - Rule::message_data_fetch => { - let mut pairs = pair.into_inner(); - let msg_att = pairs.next().unwrap(); - let attrs = msg_att.into_inner().map(build_msg_att).collect(); - Response::Fetch(seq, attrs) - } - _ => unreachable!("{:#?}", pair), - } - } - _ => unreachable!("{:#?}", pair), - } - } - Rule::continue_req => { - let (code, s) = build_resp_text(unwrap1(pair)); - Response::Continue { - code, - information: Some(s), - } - } - _ => unreachable!("{:#?}", pair), - } -} - -fn build_resp_text(pair: Pair) -> (Option, String) { - assert!(matches!(pair.as_rule(), Rule::resp_text)); - let mut pairs = pair.into_inner(); - let mut pair = pairs.next().unwrap(); - let mut resp_code = None; - if let Rule::resp_text_code = pair.as_rule() { - resp_code = build_resp_text_code(pair); - pair = pairs.next().unwrap(); - } - assert!(matches!(pair.as_rule(), Rule::text)); - let s = pair.as_str().to_owned(); - (resp_code, s) -} - -fn build_msg_att(pair: Pair) -> AttributeValue { - if !matches!(pair.as_rule(), Rule::msg_att_dyn_or_stat) { - unreachable!("{:#?}", pair); - } - - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - - match pair.as_rule() { - Rule::msg_att_dynamic => AttributeValue::Flags(pair.into_inner().map(build_flag).collect()), - Rule::msg_att_static => build_msg_att_static(pair), - _ => unreachable!("{:#?}", pair), - } -} - -fn build_msg_att_static(pair: Pair) -> AttributeValue { - if !matches!(pair.as_rule(), Rule::msg_att_static) { - unreachable!("{:#?}", pair); - } - - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - - match pair.as_rule() { - Rule::msg_att_static_internaldate => { - AttributeValue::InternalDate(build_string(unwrap1(pair))) - } - Rule::msg_att_static_rfc822_size => AttributeValue::Rfc822Size(build_number(unwrap1(pair))), - Rule::msg_att_static_envelope => AttributeValue::Envelope(build_envelope(unwrap1(pair))), - // TODO: do this - Rule::msg_att_static_body => AttributeValue::BodySection { - section: None, - index: None, - data: None, - }, - _ => unreachable!("{:#?}", pair), - } -} - -fn build_envelope(_pair: Pair) -> Envelope { - // TODO: do this - Envelope::default() -} - -fn build_resp_cond_state(pair: Pair) -> (Status, Option, Option) { - if !matches!(pair.as_rule(), Rule::resp_cond_state) { - unreachable!("{:#?}", pair); - } - - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - let status = build_status(pair); - let mut code = None; - let mut information = None; - - let pair = pairs.next().unwrap(); - let pairs = pair.into_inner(); - for pair in pairs { - match pair.as_rule() { - Rule::resp_text_code => code = build_resp_text_code(pair), - Rule::text => information = Some(pair.as_str().to_owned()), - _ => unreachable!("{:#?}", pair), - } - } - - (status, code, information) -} - -fn build_resp_text_code(pair: Pair) -> Option { - if !matches!(pair.as_rule(), Rule::resp_text_code) { - unreachable!("{:#?}", pair); - } - - let mut pairs = pair.into_inner(); - let pair = pairs.next()?; - Some(match pair.as_rule() { - Rule::capability_data => ResponseCode::Capabilities(build_capabilities(pair)), - Rule::resp_text_code_readwrite => ResponseCode::ReadWrite, - Rule::resp_text_code_uidvalidity => ResponseCode::UidValidity(build_number(unwrap1(pair))), - Rule::resp_text_code_uidnext => ResponseCode::UidNext(build_number(unwrap1(pair))), - Rule::resp_text_code_unseen => ResponseCode::Unseen(build_number(unwrap1(pair))), - // TODO: maybe have an actual type for these flags instead of just string - Rule::resp_text_code_permanentflags => { - ResponseCode::PermanentFlags(pair.into_inner().map(|p| p.as_str().to_owned()).collect()) - } - Rule::resp_text_code_other => { - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - let a = pair.as_str().to_owned(); - let mut b = None; - if let Some(pair) = pairs.next() { - b = Some(pair.as_str().to_owned()); - } - ResponseCode::Other(a, b) - } - _ => unreachable!("{:#?}", pair), - }) -} - -fn build_capability(pair: Pair) -> Capability { - if !matches!(pair.as_rule(), Rule::capability) { - unreachable!("{:#?}", pair); - } - - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - match pair.as_rule() { - Rule::auth_type => Capability::Auth(pair.as_str().to_uppercase().to_owned()), - Rule::atom => match pair.as_str() { - "IMAP4rev1" => Capability::Imap4rev1, - s => Capability::Atom(s.to_uppercase().to_owned()), - }, - _ => unreachable!("{:?}", pair), - } -} - -fn build_capabilities(pair: Pair) -> Vec { - if !matches!(pair.as_rule(), Rule::capability_data) { - unreachable!("{:#?}", pair); - } - - pair.into_inner().map(build_capability).collect() -} - -fn build_status(pair: Pair) -> Status { - match pair.as_rule() { - Rule::resp_status => match pair.as_str().to_uppercase().as_str() { - "OK" => Status::Ok, - "NO" => Status::No, - "BAD" => Status::Bad, - s => unreachable!("invalid status {:?}", s), - }, - _ => unreachable!("{:?}", pair), - } -} - -fn build_flag_list(pair: Pair) -> Vec { - if !matches!(pair.as_rule(), Rule::flag_list) { - unreachable!("{:#?}", pair); - } - - pair.into_inner().map(build_flag).collect() -} - -fn build_flag(mut pair: Pair) -> MailboxFlag { - if matches!(pair.as_rule(), Rule::flag_fetch) { - let mut pairs = pair.into_inner(); - pair = pairs.next().unwrap(); - - if matches!(pair.as_rule(), Rule::flag_fetch_recent) { - return MailboxFlag::Recent; - } - } - - if !matches!(pair.as_rule(), Rule::flag) { - unreachable!("{:#?}", pair); - } - - match pair.as_str() { - "\\Answered" => MailboxFlag::Answered, - "\\Flagged" => MailboxFlag::Flagged, - "\\Deleted" => MailboxFlag::Deleted, - "\\Seen" => MailboxFlag::Seen, - "\\Draft" => MailboxFlag::Draft, - // s if s.starts_with("\\") => MailboxFlag::Ext(s.to_owned()), - // TODO: what?? - s => MailboxFlag::Ext(s.to_owned()), - } -} - -fn build_mailbox_data(pair: Pair) -> MailboxData { - if !matches!(pair.as_rule(), Rule::mailbox_data) { - unreachable!("{:#?}", pair); - } - - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - match pair.as_rule() { - Rule::mailbox_data_exists => MailboxData::Exists(build_number(unwrap1(pair))), - Rule::mailbox_data_flags => { - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - let flags = build_flag_list(pair); - MailboxData::Flags(flags) - } - Rule::mailbox_data_recent => MailboxData::Recent(build_number(unwrap1(pair))), - Rule::mailbox_data_list => { - let mut pairs = pair.into_inner(); - let pair = pairs.next().unwrap(); - let (flags, delimiter, name) = build_mailbox_list(pair); - MailboxData::List { - flags, - delimiter, - name, - } - } - _ => unreachable!("{:#?}", pair), - } -} - -fn build_mailbox_list(pair: Pair) -> (Vec, Option, String) { - if !matches!(pair.as_rule(), Rule::mailbox_list) { - unreachable!("{:#?}", pair); - } - - let mut pairs = pair.into_inner(); - let mut pair = pairs.next().unwrap(); - - // let mut flags = Vec::new(); - let flags = if let Rule::mailbox_list_flags = pair.as_rule() { - let pairs_ = pair.into_inner(); - let mut flags = Vec::new(); - for pair in pairs_ { - flags.extend(build_mbx_list_flags(pair)); - } - pair = pairs.next().unwrap(); - flags - } else { - Vec::new() - }; - - assert!(matches!(pair.as_rule(), Rule::mailbox_list_string)); - let s = build_nstring(pair); - - pair = pairs.next().unwrap(); - assert!(matches!(pair.as_rule(), Rule::mailbox)); - let mailbox = build_string(pair); - - (flags, s, mailbox) -} - -fn build_mbx_list_flags(pair: Pair) -> Vec { - assert!(matches!(pair.as_rule(), Rule::mbx_list_flags)); - pair.into_inner() - .map(|pair| pair.as_str().to_owned()) - .collect() -} - -/// Unwraps a singleton pair (a pair that only has one element in its `inner` list) -fn unwrap1(pair: Pair) -> Pair { - let mut pairs = pair.into_inner(); - pairs.next().unwrap() -} - -/// Extracts a numerical type, generic over anything that could possibly be read as a number -// TODO: should probably restrict this to a few cases -fn build_number(pair: Pair) -> T -where - T: FromStr, - T::Err: Debug, -{ - if !matches!(pair.as_rule(), Rule::nz_number | Rule::number) { - unreachable!("not a number {:#?}", pair); - } - pair.as_str().parse::().unwrap() -} - -/// Wrapper around [build_string][1], except return None for the `nil` case -/// -/// [1]: self::build_string -fn build_nstring(pair: Pair) -> Option { - if matches!(pair.as_rule(), Rule::nil) { - return None; - } - Some(build_string(pair)) -} - -/// Extracts a string-type, discarding the surrounding quotes and unescaping the escaped characters -fn build_string(pair: Pair) -> String { - // TODO: actually get rid of the quotes and escaped chars - pair.as_str().to_owned() -} - -fn parse_literal(s: impl AsRef) -> ParseResult { - let mut pairs = Rfc3501::parse(Rule::literal, s.as_ref())?; - let pair = pairs.next().unwrap(); - Ok(build_literal(pair)) -} - -fn build_literal(pair: Pair) -> String { - assert!(matches!(pair.as_rule(), Rule::literal)); - - let mut pairs = pair.into_inner(); - let _ = pairs.next().unwrap(); - let literal_str = pairs.next().unwrap(); - literal_str.as_str().to_owned() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::response::*; - use pest::Parser; - - #[test] - fn test_literal() { - assert_eq!(parse_literal("{7}\r\nhellosu"), Ok("hellosu".to_owned())); - } - - #[test] - #[rustfmt::skip] - fn test_capability() { - assert_eq!(parse_capability("IMAP4rev1"), Ok(Capability::Imap4rev1)); - assert_eq!(parse_capability("LOGINDISABLED"), Ok(Capability::Atom("LOGINDISABLED".to_owned()))); - assert_eq!(parse_capability("AUTH=PLAIN"), Ok(Capability::Auth("PLAIN".to_owned()))); - assert_eq!(parse_capability("auth=plain"), Ok(Capability::Auth("PLAIN".to_owned()))); - - assert!(parse_capability("(OSU)").is_err()); - assert!(parse_capability("\x01HELLO").is_err()); - } - - #[test] - #[rustfmt::skip] - fn test_nil() { - assert!(Rfc3501::parse(Rule::nil, "NIL").is_ok()); - assert!(Rfc3501::parse(Rule::nil, "anything else").is_err()); - } - - #[test] - fn test_section_8() { - // this little exchange is from section 8 of rfc3501 - // https://tools.ietf.org/html/rfc3501#section-8 - - assert_eq!( - parse_response("* OK IMAP4rev1 Service Ready\r\n"), - Ok(Response::Data { - status: Status::Ok, - code: None, - information: Some("IMAP4rev1 Service Ready".to_owned()), - }) - ); - - assert_eq!( - parse_response("a001 OK LOGIN completed\r\n"), - Ok(Response::Done { - tag: "a001".to_owned(), - status: Status::Ok, - code: None, - information: Some("LOGIN completed".to_owned()), - }) - ); - - assert_eq!( - parse_response("* 18 EXISTS\r\n"), - Ok(Response::MailboxData(MailboxData::Exists(18))) - ); - - assert_eq!( - parse_response("* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n"), - Ok(Response::MailboxData(MailboxData::Flags(vec![ - MailboxFlag::Answered, - MailboxFlag::Flagged, - MailboxFlag::Deleted, - MailboxFlag::Seen, - MailboxFlag::Draft, - ]))) - ); - - assert_eq!( - parse_response("* 2 RECENT\r\n"), - Ok(Response::MailboxData(MailboxData::Recent(2))) - ); - - assert_eq!( - parse_response("* OK [UNSEEN 17] Message 17 is the first unseen message\r\n"), - Ok(Response::Data { - status: Status::Ok, - code: Some(ResponseCode::Unseen(17)), - information: Some("Message 17 is the first unseen message".to_owned()), - }) - ); - - assert_eq!( - parse_response("* OK [UIDVALIDITY 3857529045] UIDs valid\r\n"), - Ok(Response::Data { - status: Status::Ok, - code: Some(ResponseCode::UidValidity(3857529045)), - information: Some("UIDs valid".to_owned()), - }) - ); - - assert_eq!( - parse_response("a002 OK [READ-WRITE] SELECT completed\r\n"), - Ok(Response::Done { - tag: "a002".to_owned(), - status: Status::Ok, - code: Some(ResponseCode::ReadWrite), - information: Some("SELECT completed".to_owned()), - }) - ); - - assert_eq!( - parse_response(concat!( - r#"* 12 FETCH (FLAGS (\Seen) INTERNALDATE "17-Jul-1996 02:44:25 -0700" RFC822.SIZE 4286 ENVELOPE ("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)" "IMAP4rev1 WG mtg summary and minutes" (("Terry Gray" NIL "gray" "cac.washington.edu")) (("Terry Gray" NIL "gray" "cac.washington.edu")) (("Terry Gray" NIL "gray" "cac.washington.edu")) ((NIL NIL "imap" "cac.washington.edu")) ((NIL NIL "minutes" "CNRI.Reston.VA.US")("John Klensin" NIL "KLENSIN" "MIT.EDU")) NIL NIL "") BODY ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 302892))"#, - "\r\n", - )), - Ok(Response::Fetch( - 12, - vec![ - AttributeValue::Flags(vec![MailboxFlag::Seen]), - AttributeValue::InternalDate("\"17-Jul-1996 02:44:25 -0700\"".to_owned()), - AttributeValue::Rfc822Size(4286), - AttributeValue::Envelope(Envelope::default()), - AttributeValue::BodySection { - section: None, - index: None, - data: None, - }, - ] - )) - ); - } -} diff --git a/src/mail/mod.rs b/src/mail/mod.rs index 249f093..a79548f 100644 --- a/src/mail/mod.rs +++ b/src/mail/mod.rs @@ -61,6 +61,7 @@ pub async fn run_mail( } for acct in config.mail_accounts.into_iter() { + let mail2ui_tx = mail2ui_tx.clone(); let handle = tokio::spawn(async move { // debug!("opening imap connection for {:?}", acct);